From 78b025f75fc2a2d79132427c36f87979b2d5fa75 Mon Sep 17 00:00:00 2001 From: Roman Eskin Date: Mon, 22 Sep 2025 22:53:27 +1000 Subject: [PATCH 001/111] Fix SIGSEGV when running the 'execute on initplan' function (#61) Problem description: A query with an 'execute on initplan' function having a parameter that refers the other relation in the query caused SIGSEGV during execution. Root cause: As the function was executed in the initplan, during the execution the value of the parameter was not yet defined, as the initplan is processed before the main plan. Fix: When appending an initplan node for the function scan, set the subplan's 'parent_root' to NULL, isolating it from plan params of outer querie's. Now all such params don't get into the valid parameter list in SS_finalize_plan(), and the planner emits an error for such a query. --- src/backend/optimizer/plan/createplan.c | 8 +++++- .../regress/expected/function_extensions.out | 18 +++++++++++++ .../function_extensions_optimizer.out | 18 +++++++++++++ src/test/regress/sql/function_extensions.sql | 27 +++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 782b43359fe6..dba836fe40fb 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -7241,7 +7241,13 @@ append_initplan_for_function_scan(PlannerInfo *root, Path *best_path, Plan *plan subroot = (PlannerInfo *) palloc(sizeof(PlannerInfo)); memcpy(subroot, root, sizeof(PlannerInfo)); subroot->query_level++; - subroot->parent_root = root; + /* + * We set parent_root to NULL here in order to isolate the initplan + * from all params ('plan_params') of outer queries. Otherwise, we may + * recognize the parameter of the initplan function, referring to the + * outer query, as an eligible param. + */ + subroot->parent_root = NULL; /* reset subplan-related stuff */ subroot->plan_params = NIL; subroot->init_plans = NIL; diff --git a/src/test/regress/expected/function_extensions.out b/src/test/regress/expected/function_extensions.out index 195f7681b14a..bcb2638838ac 100644 --- a/src/test/regress/expected/function_extensions.out +++ b/src/test/regress/expected/function_extensions.out @@ -751,3 +751,21 @@ select count(*) from (select * from (select pg_catalog.gp_acquire_sample_rows('t (1 row) drop table test_ao3; +-- Test initplan function referring the outer query. +-- The query below previously led to a SIGSEGV. +-- start_matchsubs +-- m/ \(subselect\.c:.*\)/ +-- s/ \(subselect\.c:.*\)// +-- end_matchsubs +create table test_table(a text); +create function test_function(param_in text) returns +table (param_out text) as +$function$ +select param_in; +$function$ +language sql execute on initplan; +create table tnew as +select * from test_table l join test_function(l.a) r on l.a = r.param_out; +ERROR: plan should not reference subplan's variable +drop function test_function(param_in text); +drop table test_table; diff --git a/src/test/regress/expected/function_extensions_optimizer.out b/src/test/regress/expected/function_extensions_optimizer.out index 91fac75190bd..20d42dc43404 100644 --- a/src/test/regress/expected/function_extensions_optimizer.out +++ b/src/test/regress/expected/function_extensions_optimizer.out @@ -753,3 +753,21 @@ select count(*) from (select * from (select pg_catalog.gp_acquire_sample_rows('t (1 row) drop table test_ao3; +-- Test initplan function referring the outer query. +-- The query below previously led to a SIGSEGV. +-- start_matchsubs +-- m/ \(subselect\.c:.*\)/ +-- s/ \(subselect\.c:.*\)// +-- end_matchsubs +create table test_table(a text); +create function test_function(param_in text) returns +table (param_out text) as +$function$ +select param_in; +$function$ +language sql execute on initplan; +create table tnew as +select * from test_table l join test_function(l.a) r on l.a = r.param_out; +ERROR: plan should not reference subplan's variable +drop function test_function(param_in text); +drop table test_table; diff --git a/src/test/regress/sql/function_extensions.sql b/src/test/regress/sql/function_extensions.sql index 30edbf43a62f..7cf33afd7f4c 100644 --- a/src/test/regress/sql/function_extensions.sql +++ b/src/test/regress/sql/function_extensions.sql @@ -417,3 +417,30 @@ insert into test_ao3 values(1,2),(2,3),(3,4); select count(*) from (select * from (select pg_catalog.gp_acquire_sample_rows('test_ao3'::regclass, 400, 'f')) ss limit 1) ss1; drop table test_ao3; + +-- Test initplan function referring the outer query. +-- The query below previously led to a SIGSEGV. +-- start_matchsubs +-- m/ \(subselect\.c:.*\)/ +-- s/ \(subselect\.c:.*\)// +-- end_matchsubs + +-- start_ignore +drop table if exists test_table; +drop function if exists test_function(param_in text); +-- end_ignore + +create table test_table(a text); + +create function test_function(param_in text) returns +table (param_out text) as +$function$ +select param_in; +$function$ +language sql execute on initplan; + +create table tnew as +select * from test_table l join test_function(l.a) r on l.a = r.param_out; + +drop function test_function(param_in text); +drop table test_table; From 64ab1c71d0882306061cce5667fa5a06f974a4d1 Mon Sep 17 00:00:00 2001 From: StacyRs Date: Tue, 23 Sep 2025 13:29:42 +0300 Subject: [PATCH 002/111] Update CODE_OF_CONDUCT.md (#48) The project's Code of Conduct was updated in accordance with the recent changes approved by the Greengage DB architectural committee. --- .github/CODE_OF_CONDUCT.md | 65 ++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index c90a6f5a1253..5eaf063d68d9 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -1,50 +1,45 @@ -# Code of Conduct +# Greengage DB Community Code of Conduct -## Intro +Greengage DB is an open community project. Every Greengage DB user or developer is responsible for making the community a place where people want to come back. We expect all community members to maintain professional ethics and communicate respectfully and politely. -Standing on the shoulders of the giants means not only leveraging the code base of the most successful open source relational database (PostgreSQL) but also taking a page out of one of the most successful open source governance bodies: Apache Software Foundation (ASF). Greengage Database developer community has adopted not only the Apache License but also the following code of conduct heavily borrowing from an ASF’s: +## Community Development Principles -This code of conduct applies to all spaces that are associated with the participation in the Greengage Database open source project, including chat, all public and private mailing lists, issue trackers, wikis, blogs, Twitter, and any other communication channel used by our community. A code of conduct which is specific to in-person events (ie., conferences, meetups, etc.) is expected to be a superset of this document covering additional principles put forward by the organizers of the event(s) and landlords of the space where the event is held. +In developing code, documentation, and any other products of intellectual work, we are guided by principles that are important for the development of the community. -We expect this code of conduct to be honored by everyone who participates in the Greengage Database community formally or informally, or claims any affiliation with the project, in any activities and especially when representing the Greengage Database project, in any role. +**Collaboration** -This code is not exhaustive or complete. It serves to distil our common understanding of a collaborative, shared environment and goals. We expect it to be followed in spirit as much as in the letter so that it can enrich all of us and the technical communities in which we participate. +Anyone can join the Greengage DB community and make a contribution. -## Specific guidelines +**Equality** -We strive to: -1. **Be open** We invite anyone to participate in our community. We preferably use public methods of communication for project-related messages, unless discussing something sensitive. This applies to messages for help or project-related support, too; not only is a public support request much more likely to result in an answer to a question, it also makes sure that any inadvertent mistakes made by people answering will be more easily detected and corrected. -2. **Be empathetic**, welcoming, friendly, and patient. We work together to resolve conflict, assume good intentions, and do our best to act in an empathetic fashion. We may all experience some frustration from time to time, but we do not allow frustration to turn into a personal attack. A community where people feel uncomfortable or threatened is not a productive one. We should be respectful when dealing with other community members as well as with people outside our community. -3. **Be collaborative**. Our work will be used by other people, and in turn will depend on the work of others. When we make something for the benefit of the project, we are willing to explain to others how it works, so that they can build on the work to make it even better. Any decision we make will affect users and colleagues, and we take those consequences seriously when making decisions. -4. **Be inquisitive**. Nobody knows everything! Asking questions early avoids many problems later, so questions are encouraged, though they may be directed to the appropriate forum. Those who are asked should be responsive and helpful, within the context of our shared goal of improving Greengage Database project code. -5. **Be careful in the words that we choose**. Whether we are participating as professionals or volunteers, we value professionalism in all interactions, and take responsibility for our own speech. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behaviour are not acceptable. This includes, but is not limited to: - * Violent threats or language directed against another person. - * Sexist, racist, or otherwise discriminatory jokes and language. - * Posting sexually explicit or violent material. - * Posting (or threatening to post) other people's personally identifying information ("doxing"). - * Sharing private content, such as emails sent privately or non-publicly, or unlogged forums such as IRC channel history. - * Personal insults, especially those using racist or sexist terms. - * Unwelcome sexual attention. - * Excessive or unnecessary profanity. - * Repeated harassment of others. In general, if someone asks you to stop, then stop. - * Advocating for, or encouraging, any of the above behaviour. -6. **Be concise**. Keep in mind that what you write once will be read by hundreds of persons. Writing a short email means people can understand the conversation as efficiently as possible. Short emails should always strive to be empathetic, welcoming, friendly and patient. When a long explanation is necessary, consider adding a summary. - Try to bring new ideas to a conversation so that each mail adds something unique to the thread, keeping in mind that the rest of the thread still contains the other messages with arguments that have already been made. - Try to stay on topic, especially in discussions that are already fairly large. -7. **Step down considerately**. Members of every project come and go. When somebody leaves or disengages from the project they should tell people they are leaving and take the proper steps to ensure that others can pick up where they left off. In doing so, they should remain respectful of those who continue to participate in the project and should not misrepresent the project's goals or achievements. Likewise, community members should respect any individual's choice to leave the project. +The contribution of each community member will be assessed equally, based on its objective significance for the project. -## Diversity statement +**Opportunities** -Greengage Database project welcomes and encourages participation by everyone. We are committed to being a community that everyone feels good about joining. Although we may not be able to satisfy everyone, we will always work to treat everyone well. +Community members who contribute to Greengage DB can influence the direction of the product's development. -No matter how you identify yourself or how others perceive you: we welcome you. Though no list can hope to be comprehensive, we explicitly honour diversity in: age, culture, ethnicity, genotype, gender identity or expression, language, national origin, neurotype, phenotype, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, subculture and technical ability. +**Availability** -Though we welcome people fluent in all languages, Greengage Database project development is conducted in English. +The project code will be distributed under an open license for community members anywhere in the world. -Standards for behaviour in this community are detailed in the Code of Conduct above. We expect participants in our community to meet these standards in all their interactions and to help others to do so as well. +## Code of Conduct in Action -## Reporting guidelines +This code of conduct applies to all available channels of interaction between community members, including comments on code or documentation, the project's social media, and online and offline events that are partially or fully dedicated to Greengage DB. -While this code of conduct should be adhered to by participants, we recognize that sometimes people may have a bad day, or be unaware of some of the guidelines in this code of conduct. When that happens, you may reply to them and point out this code of conduct. Such messages may be in public or in private, whatever is most appropriate. However, regardless of whether the message is public or not, it should still adhere to the relevant parts of this code of conduct; in particular, it should not be abusive or disrespectful. +Every member of the Greengage DB community is expected to adhere to the project’s code of conduct, especially when speaking on behalf of Greengage DB in the public space. +We encourage everyone to be interested in Greengage DB, regardless of their track record or formal qualifications. This does not negate the important procedures for code and documentation quality, but it does require respectful and polite treatment. -If you believe someone is violating this code of conduct, you may reply to them and point out this code of conduct. Assume good faith; it is more likely that participants are unaware of their bad behaviour than that they intentionally try to degrade the quality of the discussion. Should there be difficulties in dealing with the situation, you may report your compliance issues in confidence to coc@greengagedb.org. This will go to an individual who is entrusted with your report. +The Greengage DB project exists thanks to the voluntary contributions of its members. Some of them may leave the project over time, and this decision should be respected in the same way as their initial involvement. We expect this code of conduct to be followed in the event of parting with the project, and the departing community member is supposed to treat those who remain with respect. + +## Appropriate Conduct + +No personal characteristics can serve as grounds for refusing to work for the benefit of the Greengage DB project or belittling your contribution to it. This code of conduct considers any attacks on a person unacceptable. These are the basic principles of communication in the community. + +Unacceptable behavior is any threat to use violence, damage social or professional reputation, expressed to an individual or a group of people. Threats to damage the Greengage DB project or its reputation or the implementation of such threats are also a violation of this code of conduct. Harassment of any kind and violation of the basic principles of communication are not tolerated in the Greengage DB community. + +In the event of receiving a warning about inappropriate behavior, it is necessary to immediately stop it. Subsequent violations of the Code of Conduct will serve as grounds for taking restrictive measures against the violator, up to and including exclusion from the community. + +## Reporting + +In many cases, simply sending a link to this code of conduct will be enough to maintain a good community culture. If you see a discussion where the code may potentially be violated, proactively send a link to it. +In some cases, violations may require issuing warnings and other restrictive measures. In this case, please contact us at **code@greengagedb.org**. From 7af2d30ab0b6a825ae02d35446a1ab1f5eda4540 Mon Sep 17 00:00:00 2001 From: Hubert Zhang Date: Wed, 15 Jul 2020 13:45:55 +0800 Subject: [PATCH 003/111] Cleanup idle reader gang after utility statements Reader gangs use local snapshot to access catalog, as a result, it will not synchronize with the sharedSnapshot from write gang which will lead to inconsistent visibility of catalog table on idle reader gang. Considering the case: select * from t, t t1; -- create a reader gang. begin; create role r1; set role r1; -- set command will also dispatched to idle reader gang When set role command dispatched to idle reader gang, reader gang cannot see the new tuple t1 in catalog table pg_auth. To fix this issue, we should drop the idle reader gangs after each utility statement which may modify the catalog table. Reviewed-by: Zhenghua Lyu (cherry picked from commit d1ba4da5ae27ffb7af80a464c0e3d62a5c540bca) --- src/backend/cdb/dispatcher/cdbdisp.c | 7 +++ src/backend/cdb/dispatcher/cdbdisp_query.c | 17 ++++++ src/include/cdb/cdbdisp.h | 1 + src/test/regress/expected/guc_gp.out | 70 ++++++++++++++++++++++ src/test/regress/sql/guc_gp.sql | 42 +++++++++++++ 5 files changed, 137 insertions(+) diff --git a/src/backend/cdb/dispatcher/cdbdisp.c b/src/backend/cdb/dispatcher/cdbdisp.c index 4fc6b42a4f2e..b0d6206ad9d9 100644 --- a/src/backend/cdb/dispatcher/cdbdisp.c +++ b/src/backend/cdb/dispatcher/cdbdisp.c @@ -327,6 +327,7 @@ cdbdisp_makeDispatcherState(bool isExtendedQuery) handle->dispatcherState->allocatedGangs = NIL; handle->dispatcherState->largestGangSize = 0; handle->dispatcherState->rootGangSize = 0; + handle->dispatcherState->destroyIdleReaderGang = false; return handle->dispatcherState; } @@ -403,6 +404,12 @@ cdbdisp_destroyDispatcherState(CdbDispatcherState *ds) RecycleGang(gp, ds->forceDestroyGang); } + /* + * Destroy all the idle reader gangs when flag destroyIdleReaderGang is true + */ + if (ds->destroyIdleReaderGang) + cdbcomponent_cleanupIdleQEs(false); + ds->allocatedGangs = NIL; ds->dispatchParams = NULL; ds->primaryResults = NULL; diff --git a/src/backend/cdb/dispatcher/cdbdisp_query.c b/src/backend/cdb/dispatcher/cdbdisp_query.c index ff804b1f8c81..5176ca06be0d 100644 --- a/src/backend/cdb/dispatcher/cdbdisp_query.c +++ b/src/backend/cdb/dispatcher/cdbdisp_query.c @@ -485,6 +485,23 @@ cdbdisp_dispatchCommandInternal(DispatchCommandQueryParms *pQueryParms, */ ds = cdbdisp_makeDispatcherState(false); + /* + * Reader gangs use local snapshot to access catalog, as a result, it will + * not synchronize with the global snapshot from write gang which will lead + * to inconsistent visibilty of catalog table. Considering the case: + * + * select * from t, t t1; -- create a reader gang. + * begin; + * create role r1; + * set role r1; -- set command will also dispatched to idle reader gang + * + * When set role command dispatched to reader gang, reader gang cannot see + * the new tuple t1 in catalog table pg_auth. + * To fix this issue, we should drop the idle reader gangs after each + * utility statement which may modify the catalog table. + */ + ds->destroyIdleReaderGang = true; + queryText = buildGpQueryString(pQueryParms, &queryTextLength); /* diff --git a/src/include/cdb/cdbdisp.h b/src/include/cdb/cdbdisp.h index 3f8febafe550..941bba27b3a1 100644 --- a/src/include/cdb/cdbdisp.h +++ b/src/include/cdb/cdbdisp.h @@ -50,6 +50,7 @@ typedef struct CdbDispatcherState #ifdef USE_ASSERT_CHECKING bool isGangDestroying; #endif + bool destroyIdleReaderGang; } CdbDispatcherState; typedef struct DispatcherInternalFuncs diff --git a/src/test/regress/expected/guc_gp.out b/src/test/regress/expected/guc_gp.out index 1152b795f136..fb94ad20d65a 100644 --- a/src/test/regress/expected/guc_gp.out +++ b/src/test/regress/expected/guc_gp.out @@ -661,3 +661,73 @@ RESET search_path; SET statement_mem = '4000MB'; ERROR: Invalid input for statement_mem, must be less than max_statement_mem (2048000 kB) RESET statement_mem; +CREATE TABLE guc_gp_t1(i int) DISTRIBUTED BY (i); +INSERT INTO guc_gp_t1 VALUES(1),(2); +-- generate an idle redaer gang by the following query +SELECT count(*) FROM guc_gp_t1, guc_gp_t1 t; + count +------- + 4 +(1 row) + +-- test create role and set role in the same transaction +BEGIN; +DROP ROLE IF EXISTS guc_gp_test_role1; +NOTICE: role "guc_gp_test_role1" does not exist, skipping +CREATE ROLE guc_gp_test_role1; +NOTICE: resource queue required -- using default resource queue "pg_default" +SET ROLE guc_gp_test_role1; +RESET ROLE; +END; +-- generate an idle redaer gang by the following query +SELECT count(*) FROM guc_gp_t1, guc_gp_t1 t; + count +------- + 4 +(1 row) + +BEGIN ISOLATION LEVEL REPEATABLE READ; +DROP ROLE IF EXISTS guc_gp_test_role2; +NOTICE: role "guc_gp_test_role2" does not exist, skipping +CREATE ROLE guc_gp_test_role2; +NOTICE: resource queue required -- using default resource queue "pg_default" +SET ROLE guc_gp_test_role2; +RESET ROLE; +END; +-- test cursor case +-- cursors are also reader gangs, but they are not idle, thus will not be +-- destroyed by utility statement. +BEGIN; +DECLARE c1 CURSOR FOR SELECT * FROM guc_gp_t1 a, guc_gp_t1 b order by a.i, b.i; +DECLARE c2 CURSOR FOR SELECT * FROM guc_gp_t1 a, guc_gp_t1 b order by a.i, b.i; +FETCH c1; + i | i +---+--- + 1 | 1 +(1 row) + +DROP ROLE IF EXISTS guc_gp_test_role1; +CREATE ROLE guc_gp_test_role1; +NOTICE: resource queue required -- using default resource queue "pg_default" +SET ROLE guc_gp_test_role1; +RESET ROLE; +FETCH c2; + i | i +---+--- + 1 | 1 +(1 row) + +FETCH c1; + i | i +---+--- + 1 | 2 +(1 row) + +FETCH c2; + i | i +---+--- + 1 | 2 +(1 row) + +END; +DROP TABLE guc_gp_t1; diff --git a/src/test/regress/sql/guc_gp.sql b/src/test/regress/sql/guc_gp.sql index 5333947c4322..6e178ed7ed89 100644 --- a/src/test/regress/sql/guc_gp.sql +++ b/src/test/regress/sql/guc_gp.sql @@ -400,3 +400,45 @@ RESET search_path; -- Try to set statement_mem > max_statement_mem SET statement_mem = '4000MB'; RESET statement_mem; + +CREATE TABLE guc_gp_t1(i int) DISTRIBUTED BY (i); +INSERT INTO guc_gp_t1 VALUES(1),(2); + +-- generate an idle redaer gang by the following query +SELECT count(*) FROM guc_gp_t1, guc_gp_t1 t; + +-- test create role and set role in the same transaction +BEGIN; +DROP ROLE IF EXISTS guc_gp_test_role1; +CREATE ROLE guc_gp_test_role1; +SET ROLE guc_gp_test_role1; +RESET ROLE; +END; + +-- generate an idle redaer gang by the following query +SELECT count(*) FROM guc_gp_t1, guc_gp_t1 t; + +BEGIN ISOLATION LEVEL REPEATABLE READ; +DROP ROLE IF EXISTS guc_gp_test_role2; +CREATE ROLE guc_gp_test_role2; +SET ROLE guc_gp_test_role2; +RESET ROLE; +END; + +-- test cursor case +-- cursors are also reader gangs, but they are not idle, thus will not be +-- destroyed by utility statement. +BEGIN; +DECLARE c1 CURSOR FOR SELECT * FROM guc_gp_t1 a, guc_gp_t1 b order by a.i, b.i; +DECLARE c2 CURSOR FOR SELECT * FROM guc_gp_t1 a, guc_gp_t1 b order by a.i, b.i; +FETCH c1; +DROP ROLE IF EXISTS guc_gp_test_role1; +CREATE ROLE guc_gp_test_role1; +SET ROLE guc_gp_test_role1; +RESET ROLE; +FETCH c2; +FETCH c1; +FETCH c2; +END; + +DROP TABLE guc_gp_t1; From 6a73b0ac96ec86680f6fb963333351afd41abe66 Mon Sep 17 00:00:00 2001 From: dimoffon Date: Fri, 26 Sep 2025 16:57:15 +0300 Subject: [PATCH 004/111] update CONTRIBUTING.md --- CONTRIBUTING.md | 72 +++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3b512ee0dc8..85a4e96f3d1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,69 +1,77 @@ # Contributing -Greengage is maintained by a core team of developers with commit rights to the [main Greengage repository](https://github.com/GreengageDB/greengage) on GitHub. At the same time, we are very eager to receive contributions from anybody in the wider Greengage community. This section covers all you need to know if you want to see your code or documentation changes be added to Greengage and appear in the future releases. +Greengage was launched and is maintained by a team of independent contributors to Greenplum. Currently, we own the [Greengage DB repository on GitHub](https://github.com/GreengageDB/greengage), appoint the members of the architectural committee and perform other work related to the organizational process in addition to software development. However, great open source projects cannot exist and last long without a strong community, **so community contributions to Greengage DB are very welcome**. In this section, you’ll find initial guidance on how to contribute to Greengage and links to additional resources that will help you get your code released as part of Greengage. ## Getting started -Greengage is developed on GitHub, and anybody wishing to contribute to it will have to [have a GitHub account](https://github.com/signup) and be familiar with [Git tools and workflow](https://wiki.postgresql.org/wiki/Working_with_Git). +To contribute to Greengage, you will need a [GitHub account](https://github.com/signup). If you haven’t used Git before, find a time to familiarize yourself with [Git tooling and workflow](https://wiki.postgresql.org/wiki/Working_with_Git) before you start. -Once you have your GitHub account, [fork](https://github.com/GreengageDB/greengage/fork) this repository so that you can have your private copy to start hacking on and to use as source of pull requests. +A private copy of the Greengage repository is required to introduce changes. To create one, [fork](https://github.com/GreengageDB/greengage/fork) our repository and work on it. Having changed something, you will be able to pull request, and someone from the architectural committee will review your contribution. To get more information on the review process, see the “Patch review” section of this document. -## Licensing of Greengage contributions +## Contributions licensing -If the contribution you're submitting is original work, you can assume that we will release it as part of an overall Greengage release available to the downstream consumers under the Apache License, Version 2.0. However, in addition to that, we may also decide to release it under a different license (such as PostgreSQL License to the upstream consumers that require it. A typical example here would be we upstreaming your contribution back to PostgreSQL community (which can be done either verbatim or your contribution being upstreamed as part of the larger changeset). +As the original author of the code, you can expect that your contribution will be released and licensed under Apache License, v. 2.0. Additionally, certain contributions valuable to the broader PostgreSQL community might be released under the PostgreSQL license. If your patch is beneficial for upstream PostgreSQL, we can offer it for review individually or include it with a set of changes. -If the contribution you're submitting is NOT original work you have to indicate the name of the license and also make sure that it is similar in terms to the Apache License 2.0. Apache Software Foundation maintains a list of these licenses under [Category A](https://www.apache.org/legal/resolved.html#category-a). In addition to that, you may be required to make proper attribution in the [NOTICE file](https://github.com/GreengageDB/greengage/blob/adb-6.x/NOTICE) file similar to [these examples](https://github.com/GreengageDB/greengage/blob/adb-6.x/NOTICE#L278). +If you are NOT the author of the code you are contributing to Greengage, please make sure you take proper licensing into account. Check the third-party license terms for similarity to the Apache License 2.0. Similar licenses are listed on the Apache Software Foundation website under [Category A](https://www.apache.org/legal/resolved.html#category-a). Note that some of these licenses require making proper attribution in the [NOTICE file](https://github.com/GreengageDB/greengage/blob/adb-6.x/NOTICE) (see examples [here](https://github.com/GreengageDB/greengage/blob/adb-6.x/NOTICE#L278)). -Finally, keep in mind that it is NEVER a good idea to remove licensing headers from the work that is not your original one. Even if you are using parts of the file that originally had a licensing header at the top you should err on the side of preserving it. As always, if you are not quite sure about the licensing implications of your contributions, feel free to reach out to us. +Do NOT remove licensing headers from any piece of work done by a third party. Even partial usage of someone else’s work may assume licensing implications. Please give the original author credit for their work by keeping the licensing headers ## Coding guidelines -Your chances of getting feedback and seeing your code merged into the project greatly depend on how granular your changes are. If you happen to have a bigger change in mind, we highly recommend creating an issue first and sharing your proposal with us before you spend a lot of time writing code. Even when your proposal gets validated by the community, we still recommend doing the actual work as a series of small, self-contained commits. This makes the reviewer's job much easier and increases the timeliness of feedback. +Before introducing a major change, it is always good to validate your idea with the architectural committee. [Create an issue on GitHub](https://github.com/GreengageDB/greengage/issues) and explain what’s on your mind before you spend hours writing code. We expect that while explaining your proposal to the committee, you’ll be specific about the approaches you are going to use and reasons behind using them. -When it comes to C and C++ parts of Greengage, we try to follow [PostgreSQL Coding Conventions](https://www.postgresql.org/docs/devel/source.html). In addition to that: +Submitting changes in small portions is the best strategy, even if you are working on a massive feature. Smaller patches can be reviewed within a week while large changesets require more time for being checked by the committee members. To get timely feedback and see your code merged into the project faster, stick to small, granular pull requests. This is also a way to show the reviewers that their job is valued and respected. - * For C and perl code, please run pgindent if necessary as specified in [README.gpdb](/src/tools/pgindent/README.gpdb). - * All Python code must pass [Pylint](https://www.pylint.org/). - * All Go code must be formatted according to [gofmt](https://golang.org/cmd/gofmt/). +To help you with the process of coding and describing your pull requests for reviewers, we have created a separate [Pull Request Submission Guidelines](https://greengagedb.org/en/blog/contributing.html) document. Please refer to it when in doubt and contact us if the document provides no answer. -We recommend using `git diff --color` when reviewing your changes so that you don't have any spurious whitespace issues in the code that you submit. +Here we mention just a few best practices we expect you to apply while contributing to Greengage DB. For detailed recommendations, please refer to Greengage DB’s [Pull Request Submission Guidelines](https://greengagedb.org/en/blog/contributing.html). -All new functionality that is contributed to Greengage should be covered by regression tests that are contributed alongside it. If you are uncertain on how to test or document your work, please raise the question in a PR and the developer community will do its best to help you. + - Follow [PostgreSQL Coding Conventions](https://www.postgresql.org/docs/devel/source.html) when writing C/C++ code for Greengage. + - Run **pgindent** for C and Perl code as per [README.gpdb](https://github.com/GreengageDB/greengage/blob/main/src/tools/pgindent/README.gpdb). + - Use [Pylint](https://www.pylint.org/) for all Python code. + - Format all Golang code in accordance with [gofmt](https://golang.org/cmd/gofmt/). -At the very minimum you should always be running `make installcheck-world` to make sure that you're not breaking anything. +Use git `diff --color` as you review your changes to avoid spurious whitespace issues in the submitted code. -## Changes applicable to upstream PostgreSQL +Regression tests are mandatory for every new feature that you contribute to Greengage. All tests covering new functionality should also be contributed to the project. Check [Pull Request Submission Guidelines](https://greengagedb.org/en/blog/contributing.html) to make sure that all tests are placed in the right folders within the project repository. If you need guidance related to testing or documenting your contributions, please explicitly include your questions in the pull request, and the architectural committee members will address them during patch review. -If the change you're working on touches functionality that is common between PostgreSQL and Greengage, you may be asked to forward-port it to PostgreSQL. This is not only so that we keep reducing the delta between the two projects, but also so that any change that is relevant to PostgreSQL can benefit from a much broader review of the upstream PostgreSQL community. In general, it is a good idea to keep both code bases handy so you can be sure whether your changes may need to be forward-ported. +At the very minimum you should always make sure that all local test runs are successful before submitting a pull request (PR) to the main repository. + +## PostgreSQL-related changes + +We prefer to get the changes related to the shared functionality of PostgreSQL and Greengage DB reviewed by the members of both communities. The larger Postgres community has more resources to help improve your patches, that’s why we may request submitting your PostgreSQL-related changes to Postgres upstream to leverage that power and reduce the delta between Greengage DB and PostgreSQL. If your contribution is likely to be forward-ported to PostgreSQL, please refer to PostgreSQL code base where appropriate. ## Patch submission -Once you are ready to share your work with the Greengage core team and the rest of the Greengage community, you should push all the commits to a branch in your own repository forked from our one and [send us a pull request](https://help.github.com/articles/about-pull-requests/). +We expect that in the repository you forked from the Greengage DB one you’ll create a branch **other than main** that will contain the changes you prepared to share with us and the rest of the community. Then [send us a pull request](https://help.github.com/articles/about-pull-requests/). -We require all pull requests to be submitted against the main branch (clearly stating if the change needs to be back-ported to STABLE branches). If the change is ONLY applicable to given STABLE branch, you may decide to submit your pull requests against an active STABLE release branch. +Do keep in mind that your patch review will slow down + - if you do not follow [Pull Request Submission Guidelines](https://greengagedb.org/en/blog/contributing.html) + - if tests are missing or copied to incorrect folders -Things which slow down patch approval - - missing to accompany tests (or reproducible steps at minimum) - - submitting the patch against STABLE branch where the fix also applies to main branch +## Patch review -## Validation checks and CI +All submitted patches are subject to review by the architectural committee members. The time required for review depends of the volume and complexity of the submitted patch: + - Up to 1 week for small/easy patches; + - Up to 4 weeks for patches of medium complexity + - Up to 8 weeks for patches of extra size or complexity. -Once you submit your pull request, you will immediately see a number of validation checks performed by our automated CI pipelines. If any of these checks fails, you will need to update your pull request to take care of the issue. Pull requests with failed validation checks are very unlikely to receive any further peer review from the community members. +Architectural committee reserves the right to decline patches without review if they introduce no valuable changes and/or contain garbage code. -If you cannot figure out why a certain validation check failed, feel free to ask us in the pull request. +Each contributed patch should get approvals from two architectural committee members before being merged into the project. Both pull request description and code will be thoroughly examined to ensure high quality and security. -## Patch review +The first reviewer might initiate a discussion proposing further improvements / changes to your pull request. You might also be asked to explain certain solutions and approaches applied as part of the proposed changeset. Discussions around pull requests along with any other communication in the Greengage DB project are covered by the Code of Conduct. Once the patch is accepted by the first reviewer, the second reviewer steps up to double-check your contribution and possibly provide a final commentary. -A submitted pull request with passing validation checks is assumed to be available for peer review. Peer review is the process that ensures that contributions to Greengage are of high quality and align well with the road map and community expectations. Every member of the Greengage community is encouraged to review pull requests and provide feedback. Since you don't have to be a core team member to be able to do that, we recommend following a stream of pull reviews to anybody who's interested in becoming a long-term contributor to Greengage. As [Linus would say](https://en.wikipedia.org/wiki/Linus's_Law) "given enough eyeballs, all bugs are shallow". +Note that the members of committee volunteer to review patches. Therefore, their availability may be limited, and reasonable delays are possible. In many cases, being proactive and asking for feedback may speed up the review process. -One outcome of the peer review could be a consensus that you need to modify your pull request in certain ways. GitHub allows you to push additional commits into a branch from which a pull request was sent. Those additional commits will be then visible to all of the reviewers. +After basic review (including feature relevancy, absence of malicious changes, etc.) and approve to run CI pipelines, the processes of patch review begins. -A peer review converges when it receives at least one +1 and no -1s votes from the participants. At that point you should expect one of the core team members to pull your changes into the project. +## Validation checks and CI -Greengage prides itself on being a collaborative, consensus-driven environment. We do not believe in vetoes and any -1 vote casted as part of the peer review has to have a detailed technical explanation of what's wrong with the change. +Your patch will undergo a series of validation checks from our automated CI pipeline. If any of them fails, you will need to change the patch you contributed so as to pass this check next time. -At any time during the patch review, you may experience delays based on the availability of reviewers and core team members. Please be patient. That being said, don't get discouraged either. If you're not getting expected feedback for a few days add a comment asking for updates on the pull request itself. +While the process is generally intuitive and enables you understand what exactly was wrong, do not hesitate to ask your reviewers for help if you don’t know why your approved patch was not merged. Use the pull request discussion to contact them. ## Direct commits to the repository -On occasion you will see core team members committing directly to the repository without going through the pull request workflow. This is reserved for small changes only and the rule of thumb we use is this: if the change touches any functionality that may result in a test failure, then it has to go through a pull request workflow. If, on the other hand, the change is in the non-functional part of the code base (such as fixing a typo inside of a comment block) core team members can decide to just commit to the repository directly. +Members of the architectural committee may sometimes commit to the repository directly, without submitting pull requests. Usually they do so to introduce minor changes (i.e. typo corrections), all major code contributions need to be submitted as pull requests and go through checks. \ No newline at end of file From d016d74884f420e227ada85437e09c04970e9d10 Mon Sep 17 00:00:00 2001 From: Maxim Michkov Date: Mon, 29 Sep 2025 19:10:53 +0300 Subject: [PATCH 005/111] Fix hash join spill file issue when rescanning (#65) Previously in some cases when rescanning a hash join was required, and the hash join was executed in multiple batches that were spilled to disk, several issues occurred when rescan happened in the middle of a batch: 1. Outer batch files (which contain outer tuples for all the next batches) were never cleared, and after rescan they were filled again. When gp_workfile_compression is enabled, this caused a BufFile to be in an unexpected state (reading instead of writing), causing an error. 2. When inner batch file is read into the in-memory hash table, it is deleted. After processing the batch, the hash table is spilled again into the batch file in case rescan is possible. However, if rescan happens in the middle of a batch, the hash table was never spilled and its contents were lost, resulting in missing data in the output. To fix these issues, when rescan is executed without rebuilding the whole hash table, clear all the outer batch files and also spill the current batch to an inner batch file. Ticket: ADBDEV-8371 --- src/backend/executor/nodeHashjoin.c | 50 ++++++++++++++---- src/test/regress/expected/bfv_joins.out | 52 +++++++++++++++++++ .../regress/expected/bfv_joins_optimizer.out | 52 +++++++++++++++++++ src/test/regress/sql/bfv_joins.sql | 18 +++++++ 4 files changed, 162 insertions(+), 10 deletions(-) diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 51138b421194..8de1f8eb12e4 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -1197,6 +1197,8 @@ ExecHashJoinGetSavedTuple(HashJoinState *hjstate, void ExecReScanHashJoin(HashJoinState *node) { + HashJoinTable hashtable = node->hj_HashTable; + /* * In a multi-batch join, we currently have to do rescans the hard way, * primarily because batch temp files may have already been released. But @@ -1204,12 +1206,12 @@ ExecReScanHashJoin(HashJoinState *node) * inner subnode, then we can just re-use the existing hash table without * rebuilding it. */ - if (node->hj_HashTable != NULL) + if (hashtable != NULL) { - node->hj_HashTable->first_pass = false; + hashtable->first_pass = false; if (node->js.ps.righttree->chgParam == NULL && - !node->hj_HashTable->eagerlyReleased) + !hashtable->eagerlyReleased) { /* * Okay to reuse the hash table; needn't rescan inner, either. @@ -1218,7 +1220,7 @@ ExecReScanHashJoin(HashJoinState *node) * inner-tuple match flags contained in the table. */ if (HJ_FILL_INNER(node)) - ExecHashTableResetMatchFlags(node->hj_HashTable); + ExecHashTableResetMatchFlags(hashtable); /* * Also, we need to reset our state about the emptiness of the @@ -1231,30 +1233,58 @@ ExecReScanHashJoin(HashJoinState *node) */ node->hj_OuterNotEmpty = false; + /* + * Outer batch files have to be cleared before restarting the hash + * join, because they will be written again when batch 0 is + * processed. + */ + if (hashtable->outerBatchFile) + { + for (int i = 0; i < hashtable->nbatch; i++) + { + if (hashtable->outerBatchFile[i]) + { + BufFileClose(hashtable->outerBatchFile[i]); + hashtable->outerBatchFile[i] = NULL; + } + } + } + /* ExecHashJoin can skip the BUILD_HASHTABLE step */ node->hj_JoinState = HJ_NEED_NEW_OUTER; - if (node->hj_HashTable->nbatch > 1) + if (hashtable->nbatch > 1) { + /* + * If we rescan in the middle of a batch, inner batch file for + * the current batch is actually deleted, its contents only + * present in the hash table. We must spill it to disk to not + * lose it. + */ + if (node->reuse_hashtable && + hashtable->innerBatchFile[hashtable->curbatch] == NULL) + { + SpillCurrentBatch(node); + } /* Force reloading batch 0 upon next ExecHashJoin */ - node->hj_HashTable->curbatch = -1; + hashtable->curbatch = -1; } else { /* MPP-1600: reset the batch number */ - node->hj_HashTable->curbatch = 0; + hashtable->curbatch = 0; } } else { /* must destroy and rebuild hash table */ - if (!node->hj_HashTable->eagerlyReleased) + if (!hashtable->eagerlyReleased) { HashState *hashState = (HashState *) innerPlanState(node); - ExecHashTableDestroy(hashState, node->hj_HashTable); + ExecHashTableDestroy(hashState, hashtable); } - pfree(node->hj_HashTable); + pfree(hashtable); node->hj_HashTable = NULL; node->hj_JoinState = HJ_BUILD_HASHTABLE; diff --git a/src/test/regress/expected/bfv_joins.out b/src/test/regress/expected/bfv_joins.out index 82f9d718f6fe..99d99a093e70 100644 --- a/src/test/regress/expected/bfv_joins.out +++ b/src/test/regress/expected/bfv_joins.out @@ -4127,6 +4127,58 @@ select * from r_table2 where ra2 in ( select a from l_table join r_table1 on b = 1 | 1 (1 row) +-- Testcase for hash join rescan clearing batch files for outer subplan +truncate l_table; +truncate r_table1; +insert into l_table select i, i%100 from generate_series(1, 20000) i; +insert into r_table1 select i%100, i from generate_series(1, 10000) i; +insert into r_table1 select i%100, i from generate_series(1, 10000) i; +analyze l_table; +analyze r_table1; +set statement_mem="1MB"; +explain analyze select * from r_table2 where ra2 in (select ra1 from l_table join r_table1 on b = rb1); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------ + Gather Motion 1:1 (slice1; segments: 1) (cost=30000000473.00..30000001205.61 rows=16 width=8) (actual time=73.649..73.651 rows=2 loops=1) + -> Nested Loop Semi Join (cost=30000000473.00..30000001205.61 rows=16 width=8) (actual time=29.419..73.334 rows=2 loops=1) + Join Filter: (r_table2.ra2 = r_table1.ra1) + -> Seq Scan on r_table2 (cost=10000000000.00..10000000001.02 rows=2 width=8) (actual time=0.003..0.004 rows=2 loops=1) + -> Hash Join (cost=20000000473.00..20000001196.00 rows=40000 width=4) (actual time=5.144..34.441 rows=20807 loops=2) + Hash Cond: (l_table.b = r_table1.rb1) + Extra Text: Initial batch 0: + Wrote 198K bytes to inner workfile. + Wrote 111K bytes to outer workfile. + Work file set: 2 files (0 compressed), avg file size 147456, compression buffer size 0 bytes + Hash chain length 3.4 avg, 14 max, using 2907 of 4096 buckets. Skipped 1 empty batches. + -> Seq Scan on l_table (cost=10000000000.00..10000000223.00 rows=20000 width=4) (actual time=0.007..15.150 rows=20000 loops=2) + -> Hash (cost=10000000223.00..10000000223.00 rows=6667 width=8) (actual time=10.240..10.240 rows=20000 loops=1) + -> Seq Scan on r_table1 (cost=10000000000.00..10000000223.00 rows=20000 width=8) (actual time=0.013..3.960 rows=20000 loops=1) + Planning time: 1.497 ms + (slice0) Executor memory: 123K bytes. + * (slice1) Executor memory: 1228K bytes (seg2). Work_mem: 525K bytes max, 575K bytes wanted. + Memory used: 1024kB + Memory wanted: 1174kB + Optimizer: Postgres query optimizer + Execution time: 74.293 ms +(21 rows) + +select * from r_table2 where ra2 in (select ra1 from l_table join r_table1 on b = rb1); + ra2 | rb2 +-----+----- + 11 | 11 + 1 | 1 +(2 rows) + +set gp_workfile_compression=on; +select * from r_table2 where ra2 in (select ra1 from l_table join r_table1 on b = rb1); + ra2 | rb2 +-----+----- + 11 | 11 + 1 | 1 +(2 rows) + +reset gp_workfile_compression; +reset statement_mem; reset optimizer; reset enable_nestloop; reset enable_bitmapscan; diff --git a/src/test/regress/expected/bfv_joins_optimizer.out b/src/test/regress/expected/bfv_joins_optimizer.out index 2c71caada8a4..cec06a68916d 100644 --- a/src/test/regress/expected/bfv_joins_optimizer.out +++ b/src/test/regress/expected/bfv_joins_optimizer.out @@ -4118,6 +4118,58 @@ select * from r_table2 where ra2 in ( select a from l_table join r_table1 on b = 1 | 1 (1 row) +-- Testcase for hash join rescan clearing batch files for outer subplan +truncate l_table; +truncate r_table1; +insert into l_table select i, i%100 from generate_series(1, 20000) i; +insert into r_table1 select i%100, i from generate_series(1, 10000) i; +insert into r_table1 select i%100, i from generate_series(1, 10000) i; +analyze l_table; +analyze r_table1; +set statement_mem="1MB"; +explain analyze select * from r_table2 where ra2 in (select ra1 from l_table join r_table1 on b = rb1); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------ + Gather Motion 1:1 (slice1; segments: 1) (cost=30000000473.00..30000001205.61 rows=16 width=8) (actual time=73.649..73.651 rows=2 loops=1) + -> Nested Loop Semi Join (cost=30000000473.00..30000001205.61 rows=16 width=8) (actual time=29.419..73.334 rows=2 loops=1) + Join Filter: (r_table2.ra2 = r_table1.ra1) + -> Seq Scan on r_table2 (cost=10000000000.00..10000000001.02 rows=2 width=8) (actual time=0.003..0.004 rows=2 loops=1) + -> Hash Join (cost=20000000473.00..20000001196.00 rows=40000 width=4) (actual time=5.144..34.441 rows=20807 loops=2) + Hash Cond: (l_table.b = r_table1.rb1) + Extra Text: Initial batch 0: + Wrote 198K bytes to inner workfile. + Wrote 111K bytes to outer workfile. + Work file set: 2 files (0 compressed), avg file size 147456, compression buffer size 0 bytes + Hash chain length 3.4 avg, 14 max, using 2907 of 4096 buckets. Skipped 1 empty batches. + -> Seq Scan on l_table (cost=10000000000.00..10000000223.00 rows=20000 width=4) (actual time=0.007..15.150 rows=20000 loops=2) + -> Hash (cost=10000000223.00..10000000223.00 rows=6667 width=8) (actual time=10.240..10.240 rows=20000 loops=1) + -> Seq Scan on r_table1 (cost=10000000000.00..10000000223.00 rows=20000 width=8) (actual time=0.013..3.960 rows=20000 loops=1) + Planning time: 1.497 ms + (slice0) Executor memory: 123K bytes. + * (slice1) Executor memory: 1228K bytes (seg2). Work_mem: 525K bytes max, 575K bytes wanted. + Memory used: 1024kB + Memory wanted: 1174kB + Optimizer: Postgres query optimizer + Execution time: 74.293 ms +(21 rows) + +select * from r_table2 where ra2 in (select ra1 from l_table join r_table1 on b = rb1); + ra2 | rb2 +-----+----- + 11 | 11 + 1 | 1 +(2 rows) + +set gp_workfile_compression=on; +select * from r_table2 where ra2 in (select ra1 from l_table join r_table1 on b = rb1); + ra2 | rb2 +-----+----- + 11 | 11 + 1 | 1 +(2 rows) + +reset gp_workfile_compression; +reset statement_mem; reset optimizer; reset enable_nestloop; reset enable_bitmapscan; diff --git a/src/test/regress/sql/bfv_joins.sql b/src/test/regress/sql/bfv_joins.sql index 81b6d3558e33..b82de31a9edd 100644 --- a/src/test/regress/sql/bfv_joins.sql +++ b/src/test/regress/sql/bfv_joins.sql @@ -575,6 +575,24 @@ set enable_seqscan=off; explain select * from r_table2 where ra2 in ( select a from l_table join r_table1 on b = rb1); select * from r_table2 where ra2 in ( select a from l_table join r_table1 on b = rb1); +-- Testcase for hash join rescan clearing batch files for outer subplan +truncate l_table; +truncate r_table1; +insert into l_table select i, i%100 from generate_series(1, 20000) i; +insert into r_table1 select i%100, i from generate_series(1, 10000) i; +insert into r_table1 select i%100, i from generate_series(1, 10000) i; +analyze l_table; +analyze r_table1; + +set statement_mem="1MB"; +explain analyze select * from r_table2 where ra2 in (select ra1 from l_table join r_table1 on b = rb1); +select * from r_table2 where ra2 in (select ra1 from l_table join r_table1 on b = rb1); + +set gp_workfile_compression=on; +select * from r_table2 where ra2 in (select ra1 from l_table join r_table1 on b = rb1); +reset gp_workfile_compression; +reset statement_mem; + reset optimizer; reset enable_nestloop; reset enable_bitmapscan; From 1c9963eb9b97c7778aaf2c2f80f12045c26da121 Mon Sep 17 00:00:00 2001 From: Artem Shapatin Date: Tue, 30 Sep 2025 20:23:26 +0700 Subject: [PATCH 006/111] fix flaky fts_segment_reset test (#62) In some cases processing of injected panic fault can take a long time. So queries, sent after sending fault, can either complete successfully or end with an error. To prevent this uncertainty the waiting mechanism was introduced in test - before sending next commands after injecting panic fault, we wait for it to be fully processed that is to trigger SIGCHLD. --- src/backend/postmaster/postmaster.c | 15 +++++++++++++ .../isolation2/expected/fts_segment_reset.out | 21 ++++++++++++++++++ src/test/isolation2/isolation2_schedule | 3 +-- src/test/isolation2/sql/fts_segment_reset.sql | 22 +++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 37ead7c8693d..0de0b7da0ced 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -3738,6 +3738,21 @@ CleanupBackend(int pid, if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) { + +#ifdef FAULT_INJECTOR + if (pmState == PM_RUN) + { + /* + * Code below is for test purposes only. If a fault is set, it + * will formally be completed without doing anything. + * + * Additional note: This check of pmState == PM_RUN is just extra + * safety measurments. If we try to access this code not from + * PM_RUN the behaviour can be unpredictable. + */ + SIMPLE_FAULT_INJECTOR("backend_abort_handling"); + } +#endif HandleChildCrash(pid, exitstatus, _("server process")); return; } diff --git a/src/test/isolation2/expected/fts_segment_reset.out b/src/test/isolation2/expected/fts_segment_reset.out index 206880fb48b4..dc662e7fd26c 100644 --- a/src/test/isolation2/expected/fts_segment_reset.out +++ b/src/test/isolation2/expected/fts_segment_reset.out @@ -29,6 +29,19 @@ select gp_inject_fault_infinite('postmaster_delay_termination_bg_writer', 'skip' Success: (1 row) +-- We need to set fault at the handler of abort signal from backend +-- so then we can wait until it is triggered. We need to do this because +-- sometimes process of calling abort() (which is issued by the injection +-- of 'panic' fault in the next lines of this test) and abortion itself can +-- take a long time to complete. So, sometimes this test can give unexpected +-- results, when you think that commands sended after issuing panic fault +-- should fail, but they are being executed without any errors. +select gp_inject_fault('backend_abort_handling', 'skip', dbid) from gp_segment_configuration where role = 'p' and content = 0; + gp_inject_fault +----------------- + Success: +(1 row) + -- Now bring down primary of seg0. There're a lot of ways to do that, in order -- to better emulate a real-world scnarios we're injecting a PANIC to do that. 1:select gp_inject_fault('start_prepare', 'panic', dbid) from gp_segment_configuration where role = 'p' AND content = 0; @@ -38,6 +51,14 @@ select gp_inject_fault_infinite('postmaster_delay_termination_bg_writer', 'skip' (1 row) 1&:create table fts_reset_t(a int); +-- We now wait till our seg0 will start reaping it's childs and resetting. +-- We can ignore the results of this command, because it can throw 3 things: +-- error because we couldn't send command because seg0 already restarting; +-- error because reaper killed backend on seg0 because it started reaping +-- backends (previous panic fault injection) +-- no error, because we managed to overrun reaper and sendeed results +-- of wait_until_triggered back + -- This should fail due to the seg0 in reset mode 2&:create table fts_reset_t2(a int); diff --git a/src/test/isolation2/isolation2_schedule b/src/test/isolation2/isolation2_schedule index dd3b99330059..80c99ffff443 100644 --- a/src/test/isolation2/isolation2_schedule +++ b/src/test/isolation2/isolation2_schedule @@ -266,8 +266,7 @@ test: pg_basebackup_with_tablespaces test: segwalrep/hintbit_throttle test: fts_manual_probe test: fts_session_reset -# FIXME: temporary disable as unstable -# test: fts_segment_reset +test: fts_segment_reset # Reindex tests test: reindex/abort_reindex diff --git a/src/test/isolation2/sql/fts_segment_reset.sql b/src/test/isolation2/sql/fts_segment_reset.sql index 135e7891375d..9c618fc454ac 100644 --- a/src/test/isolation2/sql/fts_segment_reset.sql +++ b/src/test/isolation2/sql/fts_segment_reset.sql @@ -23,12 +23,34 @@ select gp_inject_fault_infinite('postmaster_delay_termination_bg_writer', 'skip', dbid) from gp_segment_configuration where role = 'p' and content = 0; +-- We need to set fault at the handler of abort signal from backend +-- so then we can wait until it is triggered. We need to do this because +-- sometimes process of calling abort() (which is issued by the injection +-- of 'panic' fault in the next lines of this test) and abortion itself can +-- take a long time to complete. So, sometimes this test can give unexpected +-- results, when you think that commands sended after issuing panic fault +-- should fail, but they are being executed without any errors. +select gp_inject_fault('backend_abort_handling', 'skip', dbid) +from gp_segment_configuration where role = 'p' and content = 0; + -- Now bring down primary of seg0. There're a lot of ways to do that, in order -- to better emulate a real-world scnarios we're injecting a PANIC to do that. 1:select gp_inject_fault('start_prepare', 'panic', dbid) from gp_segment_configuration where role = 'p' AND content = 0; 1&:create table fts_reset_t(a int); +-- We now wait till our seg0 will start reaping it's childs and resetting. +-- We can ignore the results of this command, because it can throw 3 things: +-- error because we couldn't send command because seg0 already restarting; +-- error because reaper killed backend on seg0 because it started reaping +-- backends (previous panic fault injection) +-- no error, because we managed to overrun reaper and sendeed results +-- of wait_until_triggered back +-- start_ignore +select gp_wait_until_triggered_fault('backend_abort_handling', 1, dbid) +from gp_segment_configuration where role = 'p' and content = 0; +-- end_ignore + -- This should fail due to the seg0 in reset mode 2&:create table fts_reset_t2(a int); From e2c9aa54482800e6d5abb11ee1cde7fe49856db2 Mon Sep 17 00:00:00 2001 From: Dmitry Voronkov Date: Wed, 1 Oct 2025 10:25:38 +0300 Subject: [PATCH 007/111] update CONTRIBUTING.md (#68) --- CONTRIBUTING.md | 72 +++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3b512ee0dc8..85a4e96f3d1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,69 +1,77 @@ # Contributing -Greengage is maintained by a core team of developers with commit rights to the [main Greengage repository](https://github.com/GreengageDB/greengage) on GitHub. At the same time, we are very eager to receive contributions from anybody in the wider Greengage community. This section covers all you need to know if you want to see your code or documentation changes be added to Greengage and appear in the future releases. +Greengage was launched and is maintained by a team of independent contributors to Greenplum. Currently, we own the [Greengage DB repository on GitHub](https://github.com/GreengageDB/greengage), appoint the members of the architectural committee and perform other work related to the organizational process in addition to software development. However, great open source projects cannot exist and last long without a strong community, **so community contributions to Greengage DB are very welcome**. In this section, you’ll find initial guidance on how to contribute to Greengage and links to additional resources that will help you get your code released as part of Greengage. ## Getting started -Greengage is developed on GitHub, and anybody wishing to contribute to it will have to [have a GitHub account](https://github.com/signup) and be familiar with [Git tools and workflow](https://wiki.postgresql.org/wiki/Working_with_Git). +To contribute to Greengage, you will need a [GitHub account](https://github.com/signup). If you haven’t used Git before, find a time to familiarize yourself with [Git tooling and workflow](https://wiki.postgresql.org/wiki/Working_with_Git) before you start. -Once you have your GitHub account, [fork](https://github.com/GreengageDB/greengage/fork) this repository so that you can have your private copy to start hacking on and to use as source of pull requests. +A private copy of the Greengage repository is required to introduce changes. To create one, [fork](https://github.com/GreengageDB/greengage/fork) our repository and work on it. Having changed something, you will be able to pull request, and someone from the architectural committee will review your contribution. To get more information on the review process, see the “Patch review” section of this document. -## Licensing of Greengage contributions +## Contributions licensing -If the contribution you're submitting is original work, you can assume that we will release it as part of an overall Greengage release available to the downstream consumers under the Apache License, Version 2.0. However, in addition to that, we may also decide to release it under a different license (such as PostgreSQL License to the upstream consumers that require it. A typical example here would be we upstreaming your contribution back to PostgreSQL community (which can be done either verbatim or your contribution being upstreamed as part of the larger changeset). +As the original author of the code, you can expect that your contribution will be released and licensed under Apache License, v. 2.0. Additionally, certain contributions valuable to the broader PostgreSQL community might be released under the PostgreSQL license. If your patch is beneficial for upstream PostgreSQL, we can offer it for review individually or include it with a set of changes. -If the contribution you're submitting is NOT original work you have to indicate the name of the license and also make sure that it is similar in terms to the Apache License 2.0. Apache Software Foundation maintains a list of these licenses under [Category A](https://www.apache.org/legal/resolved.html#category-a). In addition to that, you may be required to make proper attribution in the [NOTICE file](https://github.com/GreengageDB/greengage/blob/adb-6.x/NOTICE) file similar to [these examples](https://github.com/GreengageDB/greengage/blob/adb-6.x/NOTICE#L278). +If you are NOT the author of the code you are contributing to Greengage, please make sure you take proper licensing into account. Check the third-party license terms for similarity to the Apache License 2.0. Similar licenses are listed on the Apache Software Foundation website under [Category A](https://www.apache.org/legal/resolved.html#category-a). Note that some of these licenses require making proper attribution in the [NOTICE file](https://github.com/GreengageDB/greengage/blob/adb-6.x/NOTICE) (see examples [here](https://github.com/GreengageDB/greengage/blob/adb-6.x/NOTICE#L278)). -Finally, keep in mind that it is NEVER a good idea to remove licensing headers from the work that is not your original one. Even if you are using parts of the file that originally had a licensing header at the top you should err on the side of preserving it. As always, if you are not quite sure about the licensing implications of your contributions, feel free to reach out to us. +Do NOT remove licensing headers from any piece of work done by a third party. Even partial usage of someone else’s work may assume licensing implications. Please give the original author credit for their work by keeping the licensing headers ## Coding guidelines -Your chances of getting feedback and seeing your code merged into the project greatly depend on how granular your changes are. If you happen to have a bigger change in mind, we highly recommend creating an issue first and sharing your proposal with us before you spend a lot of time writing code. Even when your proposal gets validated by the community, we still recommend doing the actual work as a series of small, self-contained commits. This makes the reviewer's job much easier and increases the timeliness of feedback. +Before introducing a major change, it is always good to validate your idea with the architectural committee. [Create an issue on GitHub](https://github.com/GreengageDB/greengage/issues) and explain what’s on your mind before you spend hours writing code. We expect that while explaining your proposal to the committee, you’ll be specific about the approaches you are going to use and reasons behind using them. -When it comes to C and C++ parts of Greengage, we try to follow [PostgreSQL Coding Conventions](https://www.postgresql.org/docs/devel/source.html). In addition to that: +Submitting changes in small portions is the best strategy, even if you are working on a massive feature. Smaller patches can be reviewed within a week while large changesets require more time for being checked by the committee members. To get timely feedback and see your code merged into the project faster, stick to small, granular pull requests. This is also a way to show the reviewers that their job is valued and respected. - * For C and perl code, please run pgindent if necessary as specified in [README.gpdb](/src/tools/pgindent/README.gpdb). - * All Python code must pass [Pylint](https://www.pylint.org/). - * All Go code must be formatted according to [gofmt](https://golang.org/cmd/gofmt/). +To help you with the process of coding and describing your pull requests for reviewers, we have created a separate [Pull Request Submission Guidelines](https://greengagedb.org/en/blog/contributing.html) document. Please refer to it when in doubt and contact us if the document provides no answer. -We recommend using `git diff --color` when reviewing your changes so that you don't have any spurious whitespace issues in the code that you submit. +Here we mention just a few best practices we expect you to apply while contributing to Greengage DB. For detailed recommendations, please refer to Greengage DB’s [Pull Request Submission Guidelines](https://greengagedb.org/en/blog/contributing.html). -All new functionality that is contributed to Greengage should be covered by regression tests that are contributed alongside it. If you are uncertain on how to test or document your work, please raise the question in a PR and the developer community will do its best to help you. + - Follow [PostgreSQL Coding Conventions](https://www.postgresql.org/docs/devel/source.html) when writing C/C++ code for Greengage. + - Run **pgindent** for C and Perl code as per [README.gpdb](https://github.com/GreengageDB/greengage/blob/main/src/tools/pgindent/README.gpdb). + - Use [Pylint](https://www.pylint.org/) for all Python code. + - Format all Golang code in accordance with [gofmt](https://golang.org/cmd/gofmt/). -At the very minimum you should always be running `make installcheck-world` to make sure that you're not breaking anything. +Use git `diff --color` as you review your changes to avoid spurious whitespace issues in the submitted code. -## Changes applicable to upstream PostgreSQL +Regression tests are mandatory for every new feature that you contribute to Greengage. All tests covering new functionality should also be contributed to the project. Check [Pull Request Submission Guidelines](https://greengagedb.org/en/blog/contributing.html) to make sure that all tests are placed in the right folders within the project repository. If you need guidance related to testing or documenting your contributions, please explicitly include your questions in the pull request, and the architectural committee members will address them during patch review. -If the change you're working on touches functionality that is common between PostgreSQL and Greengage, you may be asked to forward-port it to PostgreSQL. This is not only so that we keep reducing the delta between the two projects, but also so that any change that is relevant to PostgreSQL can benefit from a much broader review of the upstream PostgreSQL community. In general, it is a good idea to keep both code bases handy so you can be sure whether your changes may need to be forward-ported. +At the very minimum you should always make sure that all local test runs are successful before submitting a pull request (PR) to the main repository. + +## PostgreSQL-related changes + +We prefer to get the changes related to the shared functionality of PostgreSQL and Greengage DB reviewed by the members of both communities. The larger Postgres community has more resources to help improve your patches, that’s why we may request submitting your PostgreSQL-related changes to Postgres upstream to leverage that power and reduce the delta between Greengage DB and PostgreSQL. If your contribution is likely to be forward-ported to PostgreSQL, please refer to PostgreSQL code base where appropriate. ## Patch submission -Once you are ready to share your work with the Greengage core team and the rest of the Greengage community, you should push all the commits to a branch in your own repository forked from our one and [send us a pull request](https://help.github.com/articles/about-pull-requests/). +We expect that in the repository you forked from the Greengage DB one you’ll create a branch **other than main** that will contain the changes you prepared to share with us and the rest of the community. Then [send us a pull request](https://help.github.com/articles/about-pull-requests/). -We require all pull requests to be submitted against the main branch (clearly stating if the change needs to be back-ported to STABLE branches). If the change is ONLY applicable to given STABLE branch, you may decide to submit your pull requests against an active STABLE release branch. +Do keep in mind that your patch review will slow down + - if you do not follow [Pull Request Submission Guidelines](https://greengagedb.org/en/blog/contributing.html) + - if tests are missing or copied to incorrect folders -Things which slow down patch approval - - missing to accompany tests (or reproducible steps at minimum) - - submitting the patch against STABLE branch where the fix also applies to main branch +## Patch review -## Validation checks and CI +All submitted patches are subject to review by the architectural committee members. The time required for review depends of the volume and complexity of the submitted patch: + - Up to 1 week for small/easy patches; + - Up to 4 weeks for patches of medium complexity + - Up to 8 weeks for patches of extra size or complexity. -Once you submit your pull request, you will immediately see a number of validation checks performed by our automated CI pipelines. If any of these checks fails, you will need to update your pull request to take care of the issue. Pull requests with failed validation checks are very unlikely to receive any further peer review from the community members. +Architectural committee reserves the right to decline patches without review if they introduce no valuable changes and/or contain garbage code. -If you cannot figure out why a certain validation check failed, feel free to ask us in the pull request. +Each contributed patch should get approvals from two architectural committee members before being merged into the project. Both pull request description and code will be thoroughly examined to ensure high quality and security. -## Patch review +The first reviewer might initiate a discussion proposing further improvements / changes to your pull request. You might also be asked to explain certain solutions and approaches applied as part of the proposed changeset. Discussions around pull requests along with any other communication in the Greengage DB project are covered by the Code of Conduct. Once the patch is accepted by the first reviewer, the second reviewer steps up to double-check your contribution and possibly provide a final commentary. -A submitted pull request with passing validation checks is assumed to be available for peer review. Peer review is the process that ensures that contributions to Greengage are of high quality and align well with the road map and community expectations. Every member of the Greengage community is encouraged to review pull requests and provide feedback. Since you don't have to be a core team member to be able to do that, we recommend following a stream of pull reviews to anybody who's interested in becoming a long-term contributor to Greengage. As [Linus would say](https://en.wikipedia.org/wiki/Linus's_Law) "given enough eyeballs, all bugs are shallow". +Note that the members of committee volunteer to review patches. Therefore, their availability may be limited, and reasonable delays are possible. In many cases, being proactive and asking for feedback may speed up the review process. -One outcome of the peer review could be a consensus that you need to modify your pull request in certain ways. GitHub allows you to push additional commits into a branch from which a pull request was sent. Those additional commits will be then visible to all of the reviewers. +After basic review (including feature relevancy, absence of malicious changes, etc.) and approve to run CI pipelines, the processes of patch review begins. -A peer review converges when it receives at least one +1 and no -1s votes from the participants. At that point you should expect one of the core team members to pull your changes into the project. +## Validation checks and CI -Greengage prides itself on being a collaborative, consensus-driven environment. We do not believe in vetoes and any -1 vote casted as part of the peer review has to have a detailed technical explanation of what's wrong with the change. +Your patch will undergo a series of validation checks from our automated CI pipeline. If any of them fails, you will need to change the patch you contributed so as to pass this check next time. -At any time during the patch review, you may experience delays based on the availability of reviewers and core team members. Please be patient. That being said, don't get discouraged either. If you're not getting expected feedback for a few days add a comment asking for updates on the pull request itself. +While the process is generally intuitive and enables you understand what exactly was wrong, do not hesitate to ask your reviewers for help if you don’t know why your approved patch was not merged. Use the pull request discussion to contact them. ## Direct commits to the repository -On occasion you will see core team members committing directly to the repository without going through the pull request workflow. This is reserved for small changes only and the rule of thumb we use is this: if the change touches any functionality that may result in a test failure, then it has to go through a pull request workflow. If, on the other hand, the change is in the non-functional part of the code base (such as fixing a typo inside of a comment block) core team members can decide to just commit to the repository directly. +Members of the architectural committee may sometimes commit to the repository directly, without submitting pull requests. Usually they do so to introduce minor changes (i.e. typo corrections), all major code contributions need to be submitted as pull requests and go through checks. \ No newline at end of file From c4e1085d26bae201ef29191373ecf7b6b92c534f Mon Sep 17 00:00:00 2001 From: Evgeniy Ratkov Date: Thu, 2 Oct 2025 09:31:12 +0300 Subject: [PATCH 008/111] Fix double-cleanup SIGSEGV when coordinator encounters OOM (#40) When a coordinator backend encounters an OOM error during a distributed query, it triggers a cleanup sequence that can execute twice or more times, causing a use-after-free crash. The sequence of events is as follows: during normal execution cleanup in `standard_ExecutorEnd()`, the system calls `cdbdisp_destroyDispatcherState()` to clean up the dispatcher state. This function frees the results array, including the `segdbDesc` pointers that reference executor backend connections. However, an OOM error may occur inside already occuring cleanup since we are foolishly trying to allocate more memory, `AbortTransaction()`, then `cdbcomponent_recycleIdleQE()` will be called later. This abort calls `cdbdisp_cleanupDispatcherHandle()` to perform cleanup operations again. The second cleanup attempt tries to read data from executor backends using the `segdbDesc` pointers that were already freed during the first cleanup in `checkDispatchResult()` to cancel the query, resulting in a segmentation fault when dereferencing invalid memory. Additionally, the `numActiveQEs` counter can become negative during reentrant calls to `cdbcomponent_recycleIdleQE()`, causing assertion failures. When a backend encounters an OOM error, it will throw an error and abort the transaction. If the backend happens to be a coordinator, it would also attempt to cancel the distributed query first, by reading results from executor backends. It is futile, since the gang would be discarded anyways if `ERRCODE_GP_MEMPROT_KILL` is encountered, which is a sign of OOM. Fixes: - Move `numActiveQEs` decrement after a `goto`. - Prevent dispatcher handle cleanup from cancelling the dispatch in case of OOM. It is instead done by `DisconnectAndDestroyAllGangs()`, based on the new global variable. - Avoid `ABORT` dispatch for distributed transactions when the new flag is active. Without a response from coordinator, the segments will eventually trigger `ABORT` themselves. - Whether gang is destroyed depends on the error code. Specify error code for runaway cleaner cancellations, so they are treated equivalently to OOM conditions. - Unit tests are modified to avoid mocking `in_oom_error_trouble()` for every `palloc()` call. - The test makes sure there aren't any `palloc()` calls at all if the server is currently handling an OOM condition. Ticket: ADBDEV-7916 Co-authored-by: Vladimir Sarmin --- src/backend/access/transam/test/xact_test.c | 2 + src/backend/access/transam/xact.c | 9 ++--- src/backend/cdb/cdbtm.c | 7 +++- src/backend/cdb/cdbutil.c | 16 ++++++-- src/backend/cdb/dispatcher/cdbconn.c | 14 ++++--- src/backend/cdb/dispatcher/cdbdisp.c | 14 +++++-- src/backend/tcop/test/postgres_test.c | 8 ++++ src/backend/utils/error/elog.c | 31 ++++++++++++++ src/backend/utils/fmgr/test/dfmgr_test.c | 8 ++++ src/backend/utils/mmgr/aset.c | 21 ++++++++++ src/backend/utils/mmgr/runaway_cleaner.c | 19 +++++++-- .../utils/mmgr/test/memaccounting_test.c | 8 ++++ src/include/utils/elog.h | 3 ++ src/test/regress/expected/bfv_oom.out | 40 +++++++++++++++++++ src/test/regress/greengage_schedule | 1 + src/test/regress/sql/bfv_oom.sql | 33 +++++++++++++++ 16 files changed, 212 insertions(+), 22 deletions(-) create mode 100644 src/test/regress/expected/bfv_oom.out create mode 100644 src/test/regress/sql/bfv_oom.sql diff --git a/src/backend/access/transam/test/xact_test.c b/src/backend/access/transam/test/xact_test.c index cbb89b2db3d2..1bfc3a6994f2 100644 --- a/src/backend/access/transam/test/xact_test.c +++ b/src/backend/access/transam/test/xact_test.c @@ -16,6 +16,8 @@ mock_TransactionIdDidAbortForReader(TransactionId xid) return xid > 100; } +#define in_oom_error_trouble(...) (false) + #include "../xact.c" static void diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 6b5acd8cead7..8fc8a42c6d91 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -3423,16 +3423,15 @@ AbortTransaction(void) cdbcomponent_cleanupIdleQEs(false); } - /* - * If memprot decides to kill process, make sure we destroy all processes - * so that all mem/resource will be freed - */ - if (elog_geterrcode() == ERRCODE_GP_MEMPROT_KILL) + /* It's an OOM error. Get rid of all gangs and their resources. */ + if (in_oom_error_trouble()) DisconnectAndDestroyAllGangs(true); /* Release resource group slot at the end of a transaction */ if (ShouldUnassignResGroup()) UnassignResGroup(); + + reset_oom_flag(); } /* diff --git a/src/backend/cdb/cdbtm.c b/src/backend/cdb/cdbtm.c index 5b83cf391865..b7fdc2ae3165 100644 --- a/src/backend/cdb/cdbtm.c +++ b/src/backend/cdb/cdbtm.c @@ -1086,7 +1086,12 @@ rollbackDtxTransaction(void) return; } - doNotifyingAbort(); + /* Do we have enough memory for the dispatch? */ + if (!in_oom_error_trouble()) + { + doNotifyingAbort(); + } + clearAndResetGxact(); return; diff --git a/src/backend/cdb/cdbutil.c b/src/backend/cdb/cdbutil.c index 10edeed25aed..b9a63fc61514 100644 --- a/src/backend/cdb/cdbutil.c +++ b/src/backend/cdb/cdbutil.c @@ -920,9 +920,6 @@ cdbcomponent_recycleIdleQE(SegmentDatabaseDescriptor *segdbDesc, bool forceDestr cdbinfo = segdbDesc->segment_database_info; isWriter = segdbDesc->isWriter; - /* update num of active QEs */ - DECR_COUNT(cdbinfo, numActiveQEs); - oldContext = MemoryContextSwitchTo(CdbComponentsContext); if (forceDestroy || !cleanupQE(segdbDesc)) @@ -957,13 +954,24 @@ cdbcomponent_recycleIdleQE(SegmentDatabaseDescriptor *segdbDesc, bool forceDestr lastWriter = cell, cell = lnext(cell)) ; if (lastWriter) + { +#ifdef FAULT_INJECTOR + if (SIMPLE_FAULT_INJECTOR("cdb_freelist_append_oom") == FaultInjectorTypeSkip) + { + ereport(ERROR, (errcode(ERRCODE_GP_MEMPROT_KILL), + errmsg("out of memory was emulated"))); + } +#endif lappend_cell(segdbDesc->segment_database_info->freelist, lastWriter, segdbDesc); + } else segdbDesc->segment_database_info->freelist = lcons(segdbDesc, segdbDesc->segment_database_info->freelist); } + /* update num of active and idle QEs */ + DECR_COUNT(cdbinfo, numActiveQEs); INCR_COUNT(cdbinfo, numIdleQEs); MemoryContextSwitchTo(oldContext); @@ -979,6 +987,8 @@ cdbcomponent_recycleIdleQE(SegmentDatabaseDescriptor *segdbDesc, bool forceDestr markCurrentGxactWriterGangLost(); } + DECR_COUNT(cdbinfo, numActiveQEs); + MemoryContextSwitchTo(oldContext); } diff --git a/src/backend/cdb/dispatcher/cdbconn.c b/src/backend/cdb/dispatcher/cdbconn.c index 26c48b9b0e43..0c1a42893545 100644 --- a/src/backend/cdb/dispatcher/cdbconn.c +++ b/src/backend/cdb/dispatcher/cdbconn.c @@ -100,16 +100,18 @@ cdbconn_createSegmentDescriptor(struct CdbComponentDatabaseInfo *cdbinfo, int id void cdbconn_termSegmentDescriptor(SegmentDatabaseDescriptor *segdbDesc) { - CdbComponentDatabases *cdbs; - Assert(CdbComponentsContext); - cdbs = segdbDesc->segment_database_info->cdbs; + /* Don't bother, since the gang would eventually be destroyed. */ + if (!in_oom_error_trouble()) + { + CdbComponentDatabases *cdbs = segdbDesc->segment_database_info->cdbs; - /* put qe identifier to free list for reuse */ - cdbs->freeCounterList = lappend_int(cdbs->freeCounterList, segdbDesc->identifier); + cdbconn_disconnect(segdbDesc); - cdbconn_disconnect(segdbDesc); + /* Put QE identifier to free list for reuse. */ + cdbs->freeCounterList = lappend_int(cdbs->freeCounterList, segdbDesc->identifier); + } if (segdbDesc->whoami != NULL) { diff --git a/src/backend/cdb/dispatcher/cdbdisp.c b/src/backend/cdb/dispatcher/cdbdisp.c index b0d6206ad9d9..924557a63acc 100644 --- a/src/backend/cdb/dispatcher/cdbdisp.c +++ b/src/backend/cdb/dispatcher/cdbdisp.c @@ -366,8 +366,7 @@ cdbdisp_destroyDispatcherState(CdbDispatcherState *ds) if (!ds) return; #ifdef USE_ASSERT_CHECKING - /* Disallow reentrance. */ - Assert (!ds->isGangDestroying); + Assert(!ds->isGangDestroying || ds->forceDestroyGang); ds->isGangDestroying = true; #endif @@ -523,7 +522,16 @@ cleanup_dispatcher_handle(dispatcher_handle_t *h) return; } - cdbdisp_cancelDispatch(h->dispatcherState); + if (in_oom_error_trouble()) + { + /* We'll reset the session anyway. */ + h->dispatcherState->forceDestroyGang = true; + } + else + { + cdbdisp_cancelDispatch(h->dispatcherState); + } + cdbdisp_destroyDispatcherState(h->dispatcherState); } diff --git a/src/backend/tcop/test/postgres_test.c b/src/backend/tcop/test/postgres_test.c index cc0b0164fdc5..740d91e06b48 100644 --- a/src/backend/tcop/test/postgres_test.c +++ b/src/backend/tcop/test/postgres_test.c @@ -19,6 +19,14 @@ errfinish_impl(int dummy __attribute__((unused)),...) PG_RE_THROW(); } +bool __wrap_in_oom_error_trouble(void); + +bool +__wrap_in_oom_error_trouble(void) +{ + return false; +} + #include "../postgres.c" #define EXPECT_EREPORT(LOG_LEVEL) \ diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index a447b00b7964..920559a8f8d5 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -174,6 +174,15 @@ static bool openlog_done = false; static char *syslog_ident = NULL; static int syslog_facility = LOG_LOCAL0; +/* + * GPDB: + * Workaround for dispatcher cleanup routines to know whether we should take a + * shortcut and avoid allocations. + * + * Set when OOM error is encountered, reset at the end of AbortTransaction(). + */ +static bool in_oom_error = false; + static void write_syslog(int level, const char *line); #endif @@ -265,6 +274,22 @@ in_error_recursion_trouble(void) return (recursion_depth > 2); } +/* + * GPDB: + * Are we currently handling an OOM error? See in_oom_error comments. + */ +bool +in_oom_error_trouble(void) +{ + return in_oom_error; +} + +void +reset_oom_flag(void) +{ + in_oom_error = false; +} + /* * One of those fallback steps is to stop trying to localize the error * message, since there's a significant probability that that's exactly @@ -626,6 +651,12 @@ errfinish(int dummy __attribute__((unused)),...) CritSectionCount = 0; /* should be unnecessary, but... */ + if (edata->sqlerrcode == ERRCODE_GP_MEMPROT_KILL || + edata->sqlerrcode == ERRCODE_OUT_OF_MEMORY) + { + in_oom_error = true; + } + /* * Note that we leave CurrentMemoryContext set to ErrorContext. The * handler should reset it to something else soon. diff --git a/src/backend/utils/fmgr/test/dfmgr_test.c b/src/backend/utils/fmgr/test/dfmgr_test.c index abd529e695bb..cc5c927bdd50 100755 --- a/src/backend/utils/fmgr/test/dfmgr_test.c +++ b/src/backend/utils/fmgr/test/dfmgr_test.c @@ -68,6 +68,14 @@ errdetail_internal_impl(const char* fmt, ...) return 0; } +bool __wrap_in_oom_error_trouble(void); + +bool +__wrap_in_oom_error_trouble(void) +{ + return false; +} + #include "../dfmgr.c" #define EXPECT_EREPORT(LOG_LEVEL) \ diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index 8bfab3c5942e..e5add5855b0c 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -89,6 +89,7 @@ #include #include "postgres.h" +#include "utils/faultinjector.h" #include "utils/memdebug.h" #include "utils/memutils.h" #include "utils/memaccounting.h" @@ -1164,6 +1165,16 @@ AllocSetAllocImpl(MemoryContext context, Size size, bool isHeader) Size chunk_size; Size blksize; +#ifdef FAULT_INJECTOR + if (in_oom_error_trouble() && + CurrentMemoryContext != ErrorContext && + SIMPLE_FAULT_INJECTOR("fatal_on_palloc_oom") == FaultInjectorTypeSkip) + { + ereport(FATAL, (errmsg("memory allocated while handling an OOM error"), + errprintstack(true))); + } +#endif + AssertArg(AllocSetIsValid(set)); #ifdef USE_ASSERT_CHECKING if (IsUnderPostmaster && context != ErrorContext && mainthread() != 0 && !pthread_equal(main_tid, pthread_self())) @@ -1617,6 +1628,16 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size) AllocChunk chunk = AllocPointerGetChunk(pointer); Size oldsize = chunk->size; +#ifdef FAULT_INJECTOR + if (in_oom_error_trouble() && + CurrentMemoryContext != ErrorContext && + SIMPLE_FAULT_INJECTOR("fatal_on_palloc_oom") == FaultInjectorTypeSkip) + { + ereport(FATAL, + (errmsg("memory reallocated while handling an OOM error"), + errprintstack(true))); + } +#endif #ifdef USE_ASSERT_CHECKING if (IsUnderPostmaster && context != ErrorContext && mainthread() != 0 && !pthread_equal(main_tid, pthread_self())) { diff --git a/src/backend/utils/mmgr/runaway_cleaner.c b/src/backend/utils/mmgr/runaway_cleaner.c index 61b304332ce3..ca02e39ae19c 100644 --- a/src/backend/utils/mmgr/runaway_cleaner.c +++ b/src/backend/utils/mmgr/runaway_cleaner.c @@ -194,12 +194,23 @@ RunawayCleaner_StartCleanup() LWLockAcquire(ResGroupLock, LW_SHARED); ResGroupGetMemoryRunawayInfo(&str); LWLockRelease(ResGroupLock); - ereport(ERROR, (errmsg("Canceling query because of high VMEM usage. %s", str.data))); + ereport( + ERROR, + (errcode(ERRCODE_GP_MEMPROT_KILL), + errmsg("Canceling query because of high VMEM usage. %s", + str.data))); } else - ereport(ERROR, (errmsg("Canceling query because of high VMEM usage. Used: %dMB, available %dMB, red zone: %dMB", - VmemTracker_ConvertVmemChunksToMB(MySessionState->sessionVmem), VmemTracker_GetAvailableVmemMB(), - RedZoneHandler_GetRedZoneLimitMB()), errprintstack(true))); + ereport( + ERROR, + (errcode(ERRCODE_GP_MEMPROT_KILL), + errmsg( + "Canceling query because of high VMEM usage. Used: %dMB, available %dMB, red zone: %dMB", + VmemTracker_ConvertVmemChunksToMB( + MySessionState->sessionVmem), + VmemTracker_GetAvailableVmemMB(), + RedZoneHandler_GetRedZoneLimitMB()), + errprintstack(true))); } /* diff --git a/src/backend/utils/mmgr/test/memaccounting_test.c b/src/backend/utils/mmgr/test/memaccounting_test.c index ab5f4b7f7ceb..122913b8b9fe 100755 --- a/src/backend/utils/mmgr/test/memaccounting_test.c +++ b/src/backend/utils/mmgr/test/memaccounting_test.c @@ -50,6 +50,14 @@ int fwrite_mock(const char *data, Size size, Size count, FILE *file); void write_stderr_mock(const char *fmt,...); +bool __wrap_in_oom_error_trouble(void); + +bool +__wrap_in_oom_error_trouble(void) +{ + return false; +} + #include "../memaccounting.c" #include "utils/memaccounting_private.h" diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 7399945928ae..f048009a594a 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -599,6 +599,9 @@ extern void DebugFileOpen(void); extern char *unpack_sql_state(int sql_state); extern bool in_error_recursion_trouble(void); +extern bool in_oom_error_trouble(void); +extern void reset_oom_flag(void); + #ifdef HAVE_SYSLOG extern void set_syslog_parameters(const char *ident, int facility); #endif diff --git a/src/test/regress/expected/bfv_oom.out b/src/test/regress/expected/bfv_oom.out new file mode 100644 index 000000000000..76b295316e9a --- /dev/null +++ b/src/test/regress/expected/bfv_oom.out @@ -0,0 +1,40 @@ +-- start_matchsubs +-- m/WARNING:.*Any temporary tables for this session have been dropped because the gang was disconnected/ +-- s/session id \=\s*\d+/session id \= DUMMY/gm +-- end_matchsubs +CREATE TEMP TABLE t1(); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause, and no column type is suitable for a distribution key. Creating a NULL policy entry. +WARNING: creating a table with no columns. +-- Emulate an OOM error and throw FATAL on any palloc() call. +SELECT gp_inject_fault('cdb_freelist_append_oom', 'skip', dbid), + gp_inject_fault('fatal_on_palloc_oom', 'skip', dbid) + FROM gp_segment_configuration + WHERE role = 'p' AND content = -1; + gp_inject_fault | gp_inject_fault +-----------------+----------------- + Success: | Success: +(1 row) + +-- We should gracefully recover instead of entering recursion, getting SIGSEGV, +-- or trying to allocate any more memory. +DO $$ + DECLARE + rec1 RECORD; + BEGIN + FOR rec1 IN SELECT * FROM t1 + LOOP + NULL; + END LOOP; + END; +$$; +WARNING: Any temporary tables for this session have been dropped because the gang was disconnected (session id = DUMMY) +ERROR: out of memory was emulated +CONTEXT: PL/pgSQL function inline_code_block line 5 at FOR over SELECT rows +SELECT gp_inject_fault('all', 'reset', dbid) + FROM gp_segment_configuration + WHERE role = 'p' AND content = -1; + gp_inject_fault +----------------- + Success: +(1 row) + diff --git a/src/test/regress/greengage_schedule b/src/test/regress/greengage_schedule index 51d9e6f71ec2..35393aa2e4a9 100755 --- a/src/test/regress/greengage_schedule +++ b/src/test/regress/greengage_schedule @@ -195,6 +195,7 @@ test: rpt rpt_joins rpt_tpch rpt_returning bfv_dml_rpt # other sessions. Therefore the other tests in this group mustn't create # temp tables test: bfv_cte bfv_joins bfv_planner bfv_subquery bfv_legacy bfv_temp bfv_dml +test: bfv_oom test: qp_olap_mdqa qp_misc gp_recursive_cte qp_dml_joins qp_dml_oids trigger_sets_oid qp_skew qp_select diff --git a/src/test/regress/sql/bfv_oom.sql b/src/test/regress/sql/bfv_oom.sql new file mode 100644 index 000000000000..302b4f57f072 --- /dev/null +++ b/src/test/regress/sql/bfv_oom.sql @@ -0,0 +1,33 @@ +-- start_ignore +CREATE EXTENSION gp_inject_fault; +-- end_ignore + +-- start_matchsubs +-- m/WARNING:.*Any temporary tables for this session have been dropped because the gang was disconnected/ +-- s/session id \=\s*\d+/session id \= DUMMY/gm +-- end_matchsubs + +CREATE TEMP TABLE t1(); + +-- Emulate an OOM error and throw FATAL on any palloc() call. +SELECT gp_inject_fault('cdb_freelist_append_oom', 'skip', dbid), + gp_inject_fault('fatal_on_palloc_oom', 'skip', dbid) + FROM gp_segment_configuration + WHERE role = 'p' AND content = -1; + +-- We should gracefully recover instead of entering recursion, getting SIGSEGV, +-- or trying to allocate any more memory. +DO $$ + DECLARE + rec1 RECORD; + BEGIN + FOR rec1 IN SELECT * FROM t1 + LOOP + NULL; + END LOOP; + END; +$$; + +SELECT gp_inject_fault('all', 'reset', dbid) + FROM gp_segment_configuration + WHERE role = 'p' AND content = -1; From 810d51b4c5a2bf37b5a1f8f5b72b34a75f77f8a4 Mon Sep 17 00:00:00 2001 From: Maxim Michkov Date: Thu, 2 Oct 2025 17:29:28 +0300 Subject: [PATCH 009/111] Fix Orca cost model to prefer hashing smaller tables (#67) Fix Orca cost model to prefer hashing smaller tables Previously in Orca it was possible to achieve bad hash join plans that hashed a much bigger table. This happened because in Orca's cost model there is a cost associated with columns used in the join conditions, and this cost was smaller when tuples are hashed than when tuples fed from an outer child. This doesn't really make sense since it could make Orca hash a bigger table if there are enough join conditions, no matter how much bigger this table is. To make sure this never happens, increase the cost per join column for inner child, so that it is bigger than for outer child (same as cost per byte already present). Additionally, Orca increased cost per join column for outer child when spilling was predicted, which doesn't make sense either since there is no additional hashing when spilling is enabled. Postgres planner only imposes additional per-byte (or rather per-page) cost when spilling hash join, so Orca should have the same per-join-column cost for both spilling and non-spilling cases. A lot of tests are affected by this change, but for most of them only costs are changed. For some, hash joins are reordered, swapping inner and outer children, since Orca previously hashed the bigger child in some cases. In case of LOJNullRejectingZeroPlacePredicates.mdp this actually restored the old plan specified in the comment. Also add a new regress test. One common change in some tests are replacing Hash Semi Join with a regular Hash Join + Sort + GroupAggregate. There is only Left Semi Join, so swapping the inner and outer children is impossible in case of semi joins. This means that it's slightly cheaper to convert Hash Semi Join to regular Hash Join to be able to swap the children. The opposite conversion also takes place where previously GroupAggregate was used. Another common change is replacing HashJoin(table1, Broadcast(table2)) gets replaced with HashJoin(Redistribute(table1), Redistribute(table2)), adding another slice. This happens because the cost for hashing is now slightly bigger, and so Orca prefers to split hashing table2 to all segments, instead of every segment hashing all rows as it would be with Broadcast. Below are some notable changes in minidump files: - ExtractPredicateFromDisjWithComputedColumns.mdp This patch changed the join order from ((cust, sales), datedim) to ((datedim, sales), cust). All three tables are identical from Orca's point of view: they are all empty and all table scans are 24 bytes wide, so there is no reason for Orca to prefer one join order over the other since they all have the same cost. - HAWQ-TPCH-Stat-Derivation.mdp The only change in the plan is swapping children on 3rd Hash Join in the plan, one involving lineitem_ao_column_none_level0 and HashJoin(partsupp_ao_column_none_level0, part_ao_column_none_level0). lineitem_ao_column_none_level0 is predicted to have approximately 22 billion rows and the hash join is predicted to have approximately 10 billion rows, so making the hash join the inner child is good in this case, since the smaller relation is hashed. - Nested-Setops-2.mdp Same here. Two swaps were performed between dept and emp in two different places. dept contains 1 row and emp contains 10001, so it's better if dept is hashed. A Redistribute Motion was also replaced with Broadcast Motion in both cases. - TPCH-Q5.mdp Probably the best improvement out of these plans. The previous plan had this join order: ``` -> Hash Join (6,000,000 rows) -> Hash Join (300,000,000 rows) -> lineitem (1,500,000,000 rows) -> Hash Join (500,000 rows) -> supplier (2,500,000 rows) -> Hash Join (5 rows) -> nation (25 rows) -> region (1 row) -> Hash Join (100,000,000 rows) -> customer (40,000,000 rows) -> orders (100,000,000 rows) ``` which contains hashing 100 million rows twice (first order, then its hash join with customer). The new plan has no such issues: ``` -> Hash Join (6,000,000 rows) -> Hash Join (170,000,000 rows) -> lineitem (1,500,000,000 rows) -> Hash Join (20,000,000 rows) -> orders (100,000,000 rows) -> Hash Join (7,000,000 rows) -> customer (40,000,000 rows) -> Hash Join (5 rows) -> nation (25 rows) -> region (1 row) -> supplier (2,500,000 rows) ``` This plan only hashes around 30 million rows in total, much better than 200 million. Ticket: ADBDEV-8413 --- ...ayJoinOnMultiDistributionColumnsTables.mdp | 6 +- ...MultiDistributionColumnsTablesNoMotion.mdp | 6 +- ...JoinUsingOperatorsOfNonDefaultOpfamily.mdp | 4 +- ...WayJoinInferredPredsRemovedWith2Motion.mdp | 14 +- .../dxl/minidump/AddPredsInSubqueries.mdp | 6 +- .../AnySubqueryWithAllSubqueryInScalar.mdp | 22 +- .../AnySubqueryWithSubqueryInScalar.mdp | 10 +- .../minidump/BitmapIndex-ChooseHashJoin.mdp | 4 +- .../dxl/minidump/BroadcastSkewedHashjoin.mdp | 4 +- .../JoinWithInListExpand.mdp | 4 +- .../JoinWithInListNoExpand.mdp | 4 +- .../CJoinOrderDPTest/JoinOrderWithDP.mdp | 90 ++-- .../CJoinOrderDPTest/JoinOrderWithOutDP.mdp | 315 +++++++----- .../NoOpMotionUsesOnlyGroupOutputColumns.mdp | 8 +- .../RedundantMotionParallelUnionAll.mdp | 4 +- .../minidump/CTAS-With-Global-Local-Agg.mdp | 16 +- .../gporca/data/dxl/minidump/CTE-4.mdp | 6 +- .../gporca/data/dxl/minidump/CTE-5.mdp | 14 +- .../CTE-Join-Redistribute-Producer.mdp | 6 +- .../dxl/minidump/CTE-NoPushProperties.mdp | 6 +- .../data/dxl/minidump/CTE-PushProperties.mdp | 6 +- .../gporca/data/dxl/minidump/CTE-volatile.mdp | 6 +- .../data/dxl/minidump/CTE15HAReplicated.mdp | 12 +- .../data/dxl/minidump/CTE15Replicated.mdp | 12 +- .../data/dxl/minidump/CTE25Replicated.mdp | 20 +- .../data/dxl/minidump/CTE2HAReplicated.mdp | 40 +- .../data/dxl/minidump/CTE2Replicated.mdp | 40 +- .../CTEMergeGroupsCircularDeriveStats.mdp | 4 +- .../data/dxl/minidump/CTEWithMergedGroup.mdp | 6 +- .../gporca/data/dxl/minidump/CTEinlining.mdp | 4 +- .../gporca/data/dxl/minidump/CTG-Join.mdp | 2 +- .../dxl/minidump/CapGbCardToSelectCard.mdp | 10 +- .../dxl/minidump/Coalesce-With-Subquery.mdp | 14 +- .../data/dxl/minidump/CoerceToDomain.mdp | 6 +- .../ConstTblGetUnderSubqWithNoOuterRef.mdp | 8 +- .../ConstTblGetUnderSubqWithOuterRef.mdp | 4 +- .../minidump/ConvertHashToRandomInsert.mdp | 6 +- .../minidump/ConvertHashToRandomSelect.mdp | 4 +- .../CorrelatedSubqueryWithAggWindowFunc.mdp | 6 +- .../DML-With-HJ-And-UniversalChild.mdp | 14 +- .../minidump/DML-With-WindowFunc-OuterRef.mdp | 8 +- .../gporca/data/dxl/minidump/DPE-IN.mdp | 4 +- .../gporca/data/dxl/minidump/DPE-NOT-IN.mdp | 4 +- .../minidump/DPE-with-unsupported-pred.mdp | 76 +-- .../data/dxl/minidump/DPv2GreedyOnly.mdp | 8 +- .../data/dxl/minidump/DPv2MinCardOnly.mdp | 8 +- .../data/dxl/minidump/DPv2QueryOnly.mdp | 8 +- .../DQA-InnerJoin-GroupBy-HashAggregate.mdp | 10 +- .../dxl/minidump/Date-TimeStamp-HashJoin.mdp | 2 +- .../Delete-With-Limit-In-Subquery.mdp | 66 +-- .../minidump/DeleteMismatchedDistribution.mdp | 8 +- .../DeleteRandomlyDistributedTableJoin.mdp | 8 +- .../minidump/DisableBroadcastThreshold.mdp | 6 +- .../minidump/DiscardRedistributeHashJoin.mdp | 2 +- .../minidump/DynamicIndexGet-OuterRefs.mdp | 18 +- .../data/dxl/minidump/EagerAggEmptyInput.mdp | 16 +- .../data/dxl/minidump/EagerAggExpression.mdp | 8 +- .../minidump/EagerAggGroupColumnInJoin.mdp | 8 +- .../gporca/data/dxl/minidump/EagerAggMax.mdp | 16 +- .../minidump/EagerAggMaxWithNestedLoop.mdp | 104 ++-- .../data/dxl/minidump/EagerAggMinMax.mdp | 8 +- .../data/dxl/minidump/EagerAggSubquery.mdp | 10 +- .../dxl/minidump/EagerAggUnsupportedAgg.mdp | 12 +- .../dxl/minidump/EffectOfLocalPredOnJoin.mdp | 4 +- .../dxl/minidump/EffectOfLocalPredOnJoin2.mdp | 4 +- .../dxl/minidump/EffectOfLocalPredOnJoin3.mdp | 2 +- .../data/dxl/minidump/EffectsOfJoinFilter.mdp | 4 +- .../gporca/data/dxl/minidump/EqualityJoin.mdp | 4 +- .../dxl/minidump/EquiJoinOnExpr-Supported.mdp | 18 +- .../minidump/EquiJoinOnExpr-Unsupported.mdp | 6 +- .../data/dxl/minidump/Equiv-HashedDistr-1.mdp | 10 +- .../data/dxl/minidump/Equiv-HashedDistr-2.mdp | 12 +- .../data/dxl/minidump/EquivClassesAndOr.mdp | 8 +- .../dxl/minidump/EquivClassesIntersect.mdp | 4 +- .../Equivalence-class-project-over-LOJ.mdp | 12 +- .../EstimateJoinRowsForCastPredicates.mdp | 2 +- .../dxl/minidump/Except-Volatile-Func.mdp | 6 +- .../gporca/data/dxl/minidump/Except.mdp | 8 +- .../minidump/ExceptAllCompatibleDataType.mdp | 14 +- .../minidump/ExceptHashCompatibleRange.mdp | 6 +- .../data/dxl/minidump/ExpandFullOuterJoin.mdp | 16 +- .../dxl/minidump/ExpandFullOuterJoin2.mdp | 16 +- .../data/dxl/minidump/ExpandJoinOrder.mdp | 12 +- .../ExpandNAryJoinGreedyWithLOJOnly.mdp | 20 +- .../dxl/minidump/ExtractPredicateFromDisj.mdp | 14 +- ...ctPredicateFromDisjWithComputedColumns.mdp | 200 ++++---- .../data/dxl/minidump/Factorized-Preds.mdp | 4 +- .../minidump/FullJoin-NonDefaultOpfamily.mdp | 14 +- .../FullJoin-Subquery-CastedPredicates.mdp | 10 +- .../FullOuterJoin-NullRejectingLHS1.mdp | 4 +- .../FullOuterJoin-NullRejectingLHS2.mdp | 4 +- .../dxl/minidump/GreedyNAryDelayCrossJoin.mdp | 8 +- .../data/dxl/minidump/GreedyNAryJoin.mdp | 106 ++-- .../GreedyNAryJoinWithDisconnectedEdges.mdp | 16 +- .../data/dxl/minidump/GroupByOuterRef.mdp | 2 +- .../dxl/minidump/GroupingOnSameTblCol-2.mdp | 10 +- .../minidump/HAWQ-TPCH-Stat-Derivation.mdp | 399 ++++++++------- .../minidump/HJN-DPE-Bitmap-Outer-Child.mdp | 4 +- .../data/dxl/minidump/HJN-DeeperOuter.mdp | 8 +- .../minidump/HJN-Redistribute-One-Side.mdp | 4 +- .../minidump/HashJoinOnRelabeledColumns.mdp | 4 +- .../gporca/data/dxl/minidump/IN-ArrayCmp.mdp | 4 +- .../dxl/minidump/InSubqueryWithNotNullCol.mdp | 12 +- .../minidump/IndexApply-ForPartialIndex.mdp | 6 +- ...Apply-IndexCondDisjointWithHashedDistr.mdp | 4 +- ...pply-IndexCondIntersectWithHashedDistr.mdp | 4 +- .../IndexApply-IndexCondMatchHashedDistr.mdp | 4 +- ...ndexApply-IndexCondSubsetOfHashedDistr.mdp | 4 +- ...exApply-IndexCondSupersetOfHashedDistr.mdp | 4 +- .../IndexApply-InnerSelect-PartTable2.mdp | 4 +- .../data/dxl/minidump/IndexGet-OuterRefs.mdp | 2 +- .../IndexScanWithNestedCTEAndSetOp.mdp | 6 +- .../data/dxl/minidump/InferPredicates.mdp | 12 +- .../minidump/InferPredicatesBCC-oid-oid.mdp | 4 +- .../minidump/InferPredicatesBCC-txt-txt.mdp | 4 +- .../minidump/InferPredicatesBCC-vc-txt.mdp | 4 +- .../dxl/minidump/InferPredicatesBCC-vc-vc.mdp | 4 +- .../InferPredicatesBCC-vcpart-txt.mdp | 4 +- .../dxl/minidump/InferPredicatesForLimit.mdp | 6 +- .../dxl/minidump/InferPredicatesForPartSQ.mdp | 4 +- .../InferPredicatesFromExistsSubquery.mdp | 4 +- .../InferPredicatesFromMultiSubquery.mdp | 442 ++++++++-------- .../minidump/InferPredicatesInnerOfLOJ.mdp | 6 +- .../InferPredicatesIntFromExistsSubquery.mdp | 4 +- .../InferPredicatesIntFromSubquery.mdp | 4 +- .../minidump/InferPredicatesJoinSubquery.mdp | 6 +- .../minidump/InferPredicatesMultiColumns.mdp | 4 +- ...rredPredicatesConstraintSimplification.mdp | 6 +- .../InnerJoinBroadcastTableHashSpec.mdp | 10 +- .../dxl/minidump/InnerJoinOverJoinExcept.mdp | 14 +- .../minidump/InnerJoinOverJoinExceptAll.mdp | 14 +- .../InnerJoinReplicatedTableHashSpec.mdp | 12 +- .../dxl/minidump/Insert-With-HJ-CTE-Agg.mdp | 14 +- .../data/dxl/minidump/InsertNonSingleton.mdp | 8 +- .../data/dxl/minidump/Intersect-OuterRefs.mdp | 6 +- .../dxl/minidump/Intersect-Volatile-Func.mdp | 2 +- .../minidump/JOIN-NonRedistributableCol.mdp | 4 +- .../data/dxl/minidump/JOIN-Pred-Cast-Int4.mdp | 4 +- .../dxl/minidump/JOIN-Pred-Cast-Varchar.mdp | 2 +- ...OIN-cast2text-int4-Eq-cast2text-double.mdp | 4 +- .../data/dxl/minidump/JOIN-int4-Eq-double.mdp | 4 +- .../data/dxl/minidump/JOIN-int4-Eq-int2.mdp | 4 +- .../data/dxl/minidump/Join-Disj-Subqs.mdp | 2 +- .../gporca/data/dxl/minidump/Join-INDF.mdp | 4 +- .../dxl/minidump/Join-Varchar-Equality.mdp | 16 +- ...tyAssociativityCommutativityAboveLimit.mdp | 6 +- ...ArityAssociativityCommutativityAtLimit.mdp | 6 +- ...tyAssociativityCommutativityBelowLimit.mdp | 6 +- .../data/dxl/minidump/JoinCitextVarchar.mdp | 4 +- .../data/dxl/minidump/JoinColWithOnlyNDV.mdp | 4 +- .../JoinCombinedHashSpecNullsNotColocated.mdp | 12 +- ...ultOpfamiliesUsingNonDefaultOpfamilyOp.mdp | 4 +- .../data/dxl/minidump/JoinNDVRemain.mdp | 4 +- .../minidump/JoinOnViewWithCastedColumn.mdp | 6 +- ...WithCastedColumnAndSubqueryInPredicate.mdp | 6 +- ...hMixOfPushableAndNonpushablePredicates.mdp | 8 +- .../JoinOnViewWithSetReturningColumn.mdp | 8 +- .../minidump/JoinOnViewWithVolatileColumn.mdp | 8 +- ...mizationLevelGreedyNonPartTblInnerJoin.mdp | 10 +- ...imizationLevelQuery3WayHashJoinPartTbl.mdp | 6 +- ...imizationLevelQueryNonPartTblInnerJoin.mdp | 8 +- .../gporca/data/dxl/minidump/JoinOrderDPE.mdp | 6 +- .../data/dxl/minidump/JoinTinterval.mdp | 8 +- .../minidump/LASJ-Not-In-Force-Broadcast.mdp | 4 +- ...ashJoin-MultiDistKeys-WithComplexPreds.mdp | 4 +- .../data/dxl/minidump/LOJ-IsNullPred.mdp | 6 +- .../gporca/data/dxl/minidump/LOJ-PushDown.mdp | 12 +- .../gporca/data/dxl/minidump/LOJ-With-Agg.mdp | 18 +- .../LOJNonNullRejectingPredicates.mdp | 10 +- .../minidump/LOJNullRejectingPredicates.mdp | 8 +- .../LOJNullRejectingZeroPlacePredicates.mdp | 246 ++++----- .../minidump/LOJReorderComplexNestedLOJs.mdp | 16 +- .../LOJReorderWithComplexPredicate.mdp | 8 +- .../data/dxl/minidump/LOJReorderWithIDF.mdp | 8 +- .../LOJReorderWithNestedLOJAndFilter.mdp | 8 +- .../minidump/LOJReorderWithOneSidedFilter.mdp | 8 +- .../LOJReorderWithSimplePredicate.mdp | 8 +- ..._convert_outer_ref_predicate_with_NULL.mdp | 4 +- ...ert_outer_ref_predicate_with_inner_ref.mdp | 6 +- .../gporca/data/dxl/minidump/LOJ_bb_mpph.mdp | 36 +- ...vert_to_inner_with_inner_predicate_IDF.mdp | 4 +- ...o_inner_with_inner_predicate_IDF_const.mdp | 6 +- ...ert_to_inner_with_outer_predicate_INDF.mdp | 6 +- ..._inner_with_outer_predicate_INDF_const.mdp | 6 +- .../minidump/LeftJoin-DPv2-With-Select.mdp | 8 +- ...LeftJoin-UnsupportedFilter-Cardinality.mdp | 6 +- .../dxl/minidump/LeftJoin-With-Coalesce.mdp | 18 +- .../minidump/LeftJoin-With-Col-Const-Pred.mdp | 8 +- .../minidump/LeftJoin-With-Pred-On-Inner.mdp | 4 +- .../minidump/LeftJoin-With-Pred-On-Inner2.mdp | 6 +- .../LeftJoinBroadcastTableHashSpec.mdp | 18 +- .../dxl/minidump/LeftJoinDPv2JoinOrder.mdp | 6 +- .../minidump/LeftJoinNullsNotColocated.mdp | 8 +- ...tOuter2InnerUnionAllAntiSemiJoin-Tpcds.mdp | 8 +- .../LeftOuter2InnerUnionAllAntiSemiJoin.mdp | 16 +- .../dxl/minidump/MDQAs-Grouping-OrderBy.mdp | 12 +- .../data/dxl/minidump/MDQAs-Grouping.mdp | 6 +- .../gporca/data/dxl/minidump/MDQAs-Union.mdp | 14 +- .../dxl/minidump/ManyTextUnionsInSubquery.mdp | 10 +- .../dxl/minidump/MissingColStatsWithLOJ.mdp | 6 +- .../MotionHazard-MaterializeUnderResult.mdp | 4 +- ...nHazard-NoMaterializeGatherUnderResult.mdp | 4 +- ...Hazard-NoMaterializeHashAggUnderResult.mdp | 20 +- ...ionHazard-NoMaterializeSortUnderResult.mdp | 6 +- ...umnDQA-InnerJoin-GroupBy-HashAggregate.mdp | 12 +- .../minidump/MultiDistKeyJoinCardinality.mdp | 4 +- ...tiDistKeyWithOtherPredsJoinCardinality.mdp | 6 +- .../dxl/minidump/MultiLevel-IN-Subquery.mdp | 6 +- .../MultiLevelDecorrelationWithSemiJoins.mdp | 2 +- .../MultipleDampedPredJoinCardinality.mdp | 4 +- ...MultipleIndependentPredJoinCardinality.mdp | 6 +- .../MultipleUpdateWithJoinOnDistCol.mdp | 8 +- .../data/dxl/minidump/NOT-IN-ArrayCmp.mdp | 4 +- .../data/dxl/minidump/NOT-IN-NotNullBoth.mdp | 4 +- .../data/dxl/minidump/NOT-IN-NullInner.mdp | 2 +- .../data/dxl/minidump/NOT-IN-NullOuter.mdp | 2 +- .../minidump/NaryWithLojAndNonLojChilds.mdp | 10 +- .../data/dxl/minidump/Nested-Setops-2.mdp | 316 ++++++------ .../minidump/NestedJoinWithCastedColumn.mdp | 20 +- .../dxl/minidump/NestedSubqLimitBindings.mdp | 44 +- ...oBroadcastUnderGatherForWindowFunction.mdp | 6 +- .../NoDistKeyMultiPredJoinCardinality.mdp | 4 +- .../NoPushdownPredicateWithCTEAnchor.mdp | 2 +- .../dxl/minidump/NoRedistributeOnAppend.mdp | 38 +- .../dxl/minidump/Non-Hashjoinable-Pred-2.mdp | 4 +- .../gporca/data/dxl/minidump/NonSingleton.mdp | 4 +- .../NotExists-SuperflousOuterRefWithGbAgg.mdp | 4 +- .../gporca/data/dxl/minidump/NotInToLASJ.mdp | 2 +- .../OneDistKeyMultiPredJoinCardinality.mdp | 4 +- .../OuterJoinOnViewWithCastedColumn.mdp | 6 +- .../dxl/minidump/PartSelectorOnJoinSide.mdp | 6 +- .../dxl/minidump/PartSelectorOnJoinSide2.mdp | 4 +- .../PartTbl-AggWithExistentialSubquery.mdp | 12 +- .../minidump/PartTbl-AvoidRangePred-DPE.mdp | 6 +- .../data/dxl/minidump/PartTbl-DPE-GroupBy.mdp | 4 +- .../data/dxl/minidump/PartTbl-DPE-Limit.mdp | 4 +- ...Tbl-DPE-PartitionSelectorRewindability.mdp | 6 +- .../minidump/PartTbl-DPE-WindowFunction.mdp | 4 +- .../PartTbl-DisablePartSelectionJoin.mdp | 4 +- .../gporca/data/dxl/minidump/PartTbl-HJ3.mdp | 4 +- .../gporca/data/dxl/minidump/PartTbl-HJ4.mdp | 4 +- .../gporca/data/dxl/minidump/PartTbl-HJ5.mdp | 4 +- .../dxl/minidump/PartTbl-JoinOverExcept.mdp | 10 +- .../dxl/minidump/PartTbl-JoinOverGbAgg-2.mdp | 4 +- .../dxl/minidump/PartTbl-JoinOverGbAgg.mdp | 4 +- .../minidump/PartTbl-JoinOverIntersect.mdp | 8 +- .../dxl/minidump/PartTbl-JoinOverUnion-1.mdp | 4 +- .../dxl/minidump/PartTbl-JoinOverUnion-2.mdp | 4 +- .../gporca/data/dxl/minidump/PartTbl-LASJ.mdp | 8 +- .../PartTbl-LeftOuterHashJoin-DPE-IsNull.mdp | 6 +- .../dxl/minidump/PartTbl-MultiWayJoin.mdp | 12 +- .../PartTbl-MultiWayJoinWithDPE-2.mdp | 8 +- .../minidump/PartTbl-MultiWayJoinWithDPE.mdp | 202 ++++---- .../dxl/minidump/PartTbl-RangeJoinPred.mdp | 4 +- .../PartTbl-RightOuterHashJoin-DPE-IsNull.mdp | 4 +- .../data/dxl/minidump/PartTbl-SQAny.mdp | 4 +- .../data/dxl/minidump/PartTbl-SQExists.mdp | 4 +- .../data/dxl/minidump/PartTbl-SQNotExists.mdp | 4 +- .../data/dxl/minidump/PartTbl-SQScalar.mdp | 4 +- .../dxl/minidump/PartTbl-WindowFunction.mdp | 2 +- ...PushConstantSelectPredicateThruJoin-11.mdp | 4 +- ...PushConstantSelectPredicateThruJoin-12.mdp | 4 +- ...PushConstantSelectPredicateThruJoin-13.mdp | 4 +- ...PushConstantSelectPredicateThruJoin-14.mdp | 4 +- ...PushConstantSelectPredicateThruJoin-15.mdp | 4 +- .../PushConstantSelectPredicateThruJoin-2.mdp | 6 +- .../PushFilterToSemiJoinLeftChild.mdp | 4 +- .../minidump/PushGbBelowJoin-NegativeCase.mdp | 8 +- .../minidump/PushSelectDownUnionAllOfCTG.mdp | 8 +- .../Remove-Distinct-From-Subquery.mdp | 10 +- .../RemoveImpliedPredOnBCCPredicates.mdp | 8 +- .../minidump/ReplicatedHashJoinReplicated.mdp | 4 +- .../ReplicatedJoinHashDistributedTable.mdp | 4 +- .../ReplicatedJoinPartitionedTable.mdp | 4 +- .../ReplicatedJoinRandomDistributedTable.mdp | 4 +- .../ReplicatedLOJHashDistributedTable.mdp | 4 +- .../ReplicatedLOJRandomDistributedTable.mdp | 4 +- .../dxl/minidump/ReplicatedLOJReplicated.mdp | 4 +- .../data/dxl/minidump/ReplicatedTableCTE.mdp | 4 +- .../dxl/minidump/ReplicatedTableInClause.mdp | 4 +- .../dxl/minidump/RightJoinBothReplicated.mdp | 4 +- .../gporca/data/dxl/minidump/RightJoinDPS.mdp | 4 +- .../data/dxl/minidump/RightJoinHashed.mdp | 4 +- .../dxl/minidump/RightJoinNoDPSNonDistKey.mdp | 4 +- .../dxl/minidump/RightJoinRedistribute.mdp | 4 +- .../data/dxl/minidump/RightJoinReplicated.mdp | 2 +- .../gporca/data/dxl/minidump/RightJoinTVF.mdp | 14 +- .../dxl/minidump/Select-Over-CTEAnchor.mdp | 14 +- .../dxl/minidump/Select-Proj-OuterJoin.mdp | 12 +- .../data/dxl/minidump/SemiJoin2InnerJoin.mdp | 12 +- .../gporca/data/dxl/minidump/SixWayDPv2.mdp | 18 +- ...ShouldInvalidateUnresolvedDynamicScans.mdp | 8 +- .../data/dxl/minidump/SqlFuncNullReject.mdp | 4 +- .../dxl/minidump/SqlFuncPredFactorize.mdp | 4 +- .../minidump/Stat-Derivation-Leaf-Pattern.mdp | 2 +- .../dxl/minidump/Subq-JoinWithOuterRef.mdp | 14 +- .../data/dxl/minidump/Subq-On-OuterRef.mdp | 4 +- .../dxl/minidump/Subq-With-OuterRefCol.mdp | 10 +- .../data/dxl/minidump/Subq2CorrSQInLOJOn.mdp | 12 +- .../data/dxl/minidump/Subq2NotInWhereLOJ.mdp | 24 +- .../data/dxl/minidump/SubqEnforceSubplan.mdp | 2 +- .../SubqExists-With-External-Corrs.mdp | 12 +- .../SubqExists-Without-External-Corrs.mdp | 18 +- .../data/dxl/minidump/SubqInIndexPred.mdp | 10 +- .../dxl/minidump/Switch-With-Subquery.mdp | 8 +- .../TPCDS-39-InnerJoin-JoinEstimate.mdp | 2 +- .../dxl/minidump/TPCH-Partitioned-256GB.mdp | 10 +- .../gporca/data/dxl/minidump/TPCH-Q5.mdp | 475 +++++++++--------- .../minidump/TaintedReplicatedTablesCTE.mdp | 4 +- .../dxl/minidump/TimeStamp-Date-HashJoin.mdp | 2 +- .../data/dxl/minidump/Tpcds-NonPart-Q70a.mdp | 52 +- .../minidump/TranslateFilterDisjunctQuals.mdp | 6 +- ...AndTableScanIntoFilterAndOneTimeFilter.mdp | 12 +- .../TranslateOneTimeFilterConjunctQuals.mdp | 6 +- .../minidump/Union-NOT-Plus-OR-Constraint.mdp | 98 ++-- .../data/dxl/minidump/Union-On-HJNs.mdp | 30 +- .../gporca/data/dxl/minidump/UnionAll.mdp | 4 +- .../minidump/UnionAllWithTruncatedOutput.mdp | 6 +- .../data/dxl/minidump/UnionWithOuterRefs.mdp | 4 +- .../data/dxl/minidump/UnnestSQJoins.mdp | 426 ++++++++-------- .../minidump/UnsupportedStatsPredicate.mdp | 2 +- .../dxl/minidump/UpdateCardinalityAssert.mdp | 8 +- .../minidump/UpdateDistKeyWithNestedJoin.mdp | 10 +- .../data/dxl/minidump/UpdateDistrKey.mdp | 8 +- .../data/dxl/minidump/UpdateNotNullCols.mdp | 12 +- .../dxl/minidump/UpdateUniqueConstraint-2.mdp | 10 +- .../dxl/minidump/UpdateWindowGatherMerge.mdp | 8 +- .../data/dxl/minidump/UpdateWithHashJoin.mdp | 6 +- ...tionSatisfactionForUniversalInnerChild.mdp | 6 +- .../dxl/minidump/cte-duplicate-columns-2.mdp | 4 +- .../Multilevel-JoinPred-AllLevels.mdp | 4 +- .../Multilevel-JoinPred-Level1.mdp | 4 +- .../Multilevel-JoinPred-Level2.mdp | 4 +- .../Multilevel-Nary-Join.mdp | 6 +- .../libgpdbcost/src/CCostModelParamsGPDB.cpp | 4 +- src/test/regress/expected/bfv_joins.out | 20 + .../regress/expected/bfv_joins_optimizer.out | 60 ++- .../expected/bfv_subquery_optimizer.out | 34 +- src/test/regress/expected/join_optimizer.out | 120 ++--- src/test/regress/expected/notin_optimizer.out | 12 +- .../qp_correlated_query_optimizer.out | 166 +++--- .../regress/expected/qp_query_execution.out | 2 +- src/test/regress/expected/rpt_optimizer.out | 46 +- .../expected/subselect_gp_optimizer.out | 75 +-- .../regress/expected/update_gp_optimizer.out | 14 +- src/test/regress/sql/bfv_joins.sql | 12 + src/test/regress/sql/qp_query_execution.sql | 2 +- 347 files changed, 3374 insertions(+), 3254 deletions(-) diff --git a/src/backend/gporca/data/dxl/minidump/3WayJoinOnMultiDistributionColumnsTables.mdp b/src/backend/gporca/data/dxl/minidump/3WayJoinOnMultiDistributionColumnsTables.mdp index 06044c94eae1..475951ca45af 100644 --- a/src/backend/gporca/data/dxl/minidump/3WayJoinOnMultiDistributionColumnsTables.mdp +++ b/src/backend/gporca/data/dxl/minidump/3WayJoinOnMultiDistributionColumnsTables.mdp @@ -2762,7 +2762,7 @@ - + @@ -2797,7 +2797,7 @@ - + @@ -2842,7 +2842,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/3WayJoinOnMultiDistributionColumnsTablesNoMotion.mdp b/src/backend/gporca/data/dxl/minidump/3WayJoinOnMultiDistributionColumnsTablesNoMotion.mdp index 54391c6a0c68..4e5cd9228054 100644 --- a/src/backend/gporca/data/dxl/minidump/3WayJoinOnMultiDistributionColumnsTablesNoMotion.mdp +++ b/src/backend/gporca/data/dxl/minidump/3WayJoinOnMultiDistributionColumnsTablesNoMotion.mdp @@ -2762,7 +2762,7 @@ t1.a = t2.a and t2.a = t3.a and t1.b = t3.b and t2.b = t3.b; - + @@ -2797,7 +2797,7 @@ t1.a = t2.a and t2.a = t3.a and t1.b = t3.b and t2.b = t3.b; - + @@ -2842,7 +2842,7 @@ t1.a = t2.a and t2.a = t3.a and t1.b = t3.b and t2.b = t3.b; - + diff --git a/src/backend/gporca/data/dxl/minidump/3WayJoinUsingOperatorsOfNonDefaultOpfamily.mdp b/src/backend/gporca/data/dxl/minidump/3WayJoinUsingOperatorsOfNonDefaultOpfamily.mdp index b0ab8f2b1737..2abac4deb839 100644 --- a/src/backend/gporca/data/dxl/minidump/3WayJoinUsingOperatorsOfNonDefaultOpfamily.mdp +++ b/src/backend/gporca/data/dxl/minidump/3WayJoinUsingOperatorsOfNonDefaultOpfamily.mdp @@ -399,7 +399,7 @@ - + @@ -413,7 +413,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/4WayJoinInferredPredsRemovedWith2Motion.mdp b/src/backend/gporca/data/dxl/minidump/4WayJoinInferredPredsRemovedWith2Motion.mdp index d16f62c4b029..8d586464d0b6 100644 --- a/src/backend/gporca/data/dxl/minidump/4WayJoinInferredPredsRemovedWith2Motion.mdp +++ b/src/backend/gporca/data/dxl/minidump/4WayJoinInferredPredsRemovedWith2Motion.mdp @@ -4649,10 +4649,10 @@ explain select * from cs catalog_sales, it item, sr store_returns, ss store_sale - + - + @@ -4693,7 +4693,7 @@ explain select * from cs catalog_sales, it item, sr store_returns, ss store_sale - + @@ -4744,7 +4744,7 @@ explain select * from cs catalog_sales, it item, sr store_returns, ss store_sale - + @@ -4784,7 +4784,7 @@ explain select * from cs catalog_sales, it item, sr store_returns, ss store_sale - + @@ -4853,7 +4853,7 @@ explain select * from cs catalog_sales, it item, sr store_returns, ss store_sale - + @@ -4876,7 +4876,7 @@ explain select * from cs catalog_sales, it item, sr store_returns, ss store_sale - + diff --git a/src/backend/gporca/data/dxl/minidump/AddPredsInSubqueries.mdp b/src/backend/gporca/data/dxl/minidump/AddPredsInSubqueries.mdp index 1186187c9927..7034e6e9fc11 100644 --- a/src/backend/gporca/data/dxl/minidump/AddPredsInSubqueries.mdp +++ b/src/backend/gporca/data/dxl/minidump/AddPredsInSubqueries.mdp @@ -395,7 +395,7 @@ - + @@ -409,7 +409,7 @@ - + @@ -456,7 +456,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/AnySubqueryWithAllSubqueryInScalar.mdp b/src/backend/gporca/data/dxl/minidump/AnySubqueryWithAllSubqueryInScalar.mdp index aeb7a19a87f9..bf89cd2e3869 100644 --- a/src/backend/gporca/data/dxl/minidump/AnySubqueryWithAllSubqueryInScalar.mdp +++ b/src/backend/gporca/data/dxl/minidump/AnySubqueryWithAllSubqueryInScalar.mdp @@ -445,7 +445,7 @@ - + @@ -456,7 +456,7 @@ - + @@ -492,20 +492,20 @@ - + - + - + @@ -518,7 +518,7 @@ - + @@ -529,7 +529,7 @@ - + @@ -546,7 +546,7 @@ - + @@ -559,7 +559,7 @@ - + @@ -570,12 +570,12 @@ - + - + diff --git a/src/backend/gporca/data/dxl/minidump/AnySubqueryWithSubqueryInScalar.mdp b/src/backend/gporca/data/dxl/minidump/AnySubqueryWithSubqueryInScalar.mdp index 5f2a982eddd8..373a0e79e1bc 100644 --- a/src/backend/gporca/data/dxl/minidump/AnySubqueryWithSubqueryInScalar.mdp +++ b/src/backend/gporca/data/dxl/minidump/AnySubqueryWithSubqueryInScalar.mdp @@ -361,7 +361,7 @@ - + @@ -372,7 +372,7 @@ - + @@ -408,20 +408,20 @@ - + - + - + diff --git a/src/backend/gporca/data/dxl/minidump/BitmapIndex-ChooseHashJoin.mdp b/src/backend/gporca/data/dxl/minidump/BitmapIndex-ChooseHashJoin.mdp index 9d740a9a4ffa..55d14286ea6b 100644 --- a/src/backend/gporca/data/dxl/minidump/BitmapIndex-ChooseHashJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/BitmapIndex-ChooseHashJoin.mdp @@ -3459,7 +3459,7 @@ - + @@ -3479,7 +3479,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/BroadcastSkewedHashjoin.mdp b/src/backend/gporca/data/dxl/minidump/BroadcastSkewedHashjoin.mdp index b9f2191540dc..a75c805a02aa 100644 --- a/src/backend/gporca/data/dxl/minidump/BroadcastSkewedHashjoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/BroadcastSkewedHashjoin.mdp @@ -3114,7 +3114,7 @@ Intent: The join should pick up a Broadcast over Redistribute. The hashjoin is s - + @@ -3134,7 +3134,7 @@ Intent: The join should pick up a Broadcast over Redistribute. The hashjoin is s - + diff --git a/src/backend/gporca/data/dxl/minidump/CArrayExpansionTest/JoinWithInListExpand.mdp b/src/backend/gporca/data/dxl/minidump/CArrayExpansionTest/JoinWithInListExpand.mdp index 8ea4e3ab8803..b72d5adf37ed 100644 --- a/src/backend/gporca/data/dxl/minidump/CArrayExpansionTest/JoinWithInListExpand.mdp +++ b/src/backend/gporca/data/dxl/minidump/CArrayExpansionTest/JoinWithInListExpand.mdp @@ -558,7 +558,7 @@ select a,b from foo, bar where foo.a in (1,2,3,4,5,6) and foo.a = bar.c; - + @@ -572,7 +572,7 @@ select a,b from foo, bar where foo.a in (1,2,3,4,5,6) and foo.a = bar.c; - + diff --git a/src/backend/gporca/data/dxl/minidump/CArrayExpansionTest/JoinWithInListNoExpand.mdp b/src/backend/gporca/data/dxl/minidump/CArrayExpansionTest/JoinWithInListNoExpand.mdp index 805677f90b67..3c5a43f2136d 100644 --- a/src/backend/gporca/data/dxl/minidump/CArrayExpansionTest/JoinWithInListNoExpand.mdp +++ b/src/backend/gporca/data/dxl/minidump/CArrayExpansionTest/JoinWithInListNoExpand.mdp @@ -558,7 +558,7 @@ select a,b from foo, bar where foo.a in (1,2,3,4,5,6) and foo.a = bar.c; - + @@ -572,7 +572,7 @@ select a,b from foo, bar where foo.a in (1,2,3,4,5,6) and foo.a = bar.c; - + diff --git a/src/backend/gporca/data/dxl/minidump/CJoinOrderDPTest/JoinOrderWithDP.mdp b/src/backend/gporca/data/dxl/minidump/CJoinOrderDPTest/JoinOrderWithDP.mdp index acd2519ed0ae..eef46476c611 100644 --- a/src/backend/gporca/data/dxl/minidump/CJoinOrderDPTest/JoinOrderWithDP.mdp +++ b/src/backend/gporca/data/dxl/minidump/CJoinOrderDPTest/JoinOrderWithDP.mdp @@ -3666,7 +3666,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 FROM foo10 FOO10 WHERE FOO10.i2 = 'EN' ) FOO1 - + @@ -3680,7 +3680,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 FROM foo10 FOO10 WHERE FOO10.i2 = 'EN' ) FOO1 - + @@ -3719,7 +3719,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 FROM foo10 FOO10 WHERE FOO10.i2 = 'EN' ) FOO1 - + @@ -3736,7 +3736,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 FROM foo10 FOO10 WHERE FOO10.i2 = 'EN' ) FOO1 - + @@ -3778,7 +3778,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 FROM foo10 FOO10 WHERE FOO10.i2 = 'EN' ) FOO1 - + @@ -3798,7 +3798,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 FROM foo10 FOO10 WHERE FOO10.i2 = 'EN' ) FOO1 - + @@ -3818,13 +3818,49 @@ INNER JOIN (SELECT FOO10.i1 AS i1 FROM foo10 FOO10 WHERE FOO10.i2 = 'EN' ) FOO1 - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3852,7 +3888,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 FROM foo10 FOO10 WHERE FOO10.i2 = 'EN' ) FOO1 - + @@ -3959,42 +3995,6 @@ INNER JOIN (SELECT FOO10.i1 AS i1 FROM foo10 FOO10 WHERE FOO10.i2 = 'EN' ) FOO1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/backend/gporca/data/dxl/minidump/CJoinOrderDPTest/JoinOrderWithOutDP.mdp b/src/backend/gporca/data/dxl/minidump/CJoinOrderDPTest/JoinOrderWithOutDP.mdp index 5a37a3c5a423..851ce181fd5e 100644 --- a/src/backend/gporca/data/dxl/minidump/CJoinOrderDPTest/JoinOrderWithOutDP.mdp +++ b/src/backend/gporca/data/dxl/minidump/CJoinOrderDPTest/JoinOrderWithOutDP.mdp @@ -1764,10 +1764,10 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - + - + @@ -1781,7 +1781,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - + @@ -1795,13 +1795,49 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1823,7 +1859,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - + @@ -1840,13 +1876,49 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1871,7 +1943,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - + @@ -1891,13 +1963,49 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1925,7 +2033,7 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - + @@ -1952,9 +2060,9 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - + - + @@ -1977,23 +2085,55 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -2002,6 +2142,11 @@ INNER JOIN (SELECT FOO10.i1 AS i1 + + + + + @@ -2029,121 +2174,13 @@ INNER JOIN (SELECT FOO10.i1 AS i1 - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/NoOpMotionUsesOnlyGroupOutputColumns.mdp b/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/NoOpMotionUsesOnlyGroupOutputColumns.mdp index 1a5cf7967191..b783e5339c20 100644 --- a/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/NoOpMotionUsesOnlyGroupOutputColumns.mdp +++ b/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/NoOpMotionUsesOnlyGroupOutputColumns.mdp @@ -280,7 +280,7 @@ EXPLAIN SELECT b, a FROM foo UNION ALL SELECT b, a FROM foo INTERSECT ALL SELECT - + @@ -294,7 +294,7 @@ EXPLAIN SELECT b, a FROM foo UNION ALL SELECT b, a FROM foo INTERSECT ALL SELECT - + @@ -354,7 +354,7 @@ EXPLAIN SELECT b, a FROM foo UNION ALL SELECT b, a FROM foo INTERSECT ALL SELECT - + @@ -373,7 +373,7 @@ EXPLAIN SELECT b, a FROM foo UNION ALL SELECT b, a FROM foo INTERSECT ALL SELECT - + diff --git a/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/RedundantMotionParallelUnionAll.mdp b/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/RedundantMotionParallelUnionAll.mdp index f3fca3c3b9ec..f667b16ce001 100644 --- a/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/RedundantMotionParallelUnionAll.mdp +++ b/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/RedundantMotionParallelUnionAll.mdp @@ -338,7 +338,7 @@ select * from t, (select * from t union all select * from t) tt where t.c = tt.c - + @@ -358,7 +358,7 @@ select * from t, (select * from t union all select * from t) tt where t.c = tt.c - + diff --git a/src/backend/gporca/data/dxl/minidump/CTAS-With-Global-Local-Agg.mdp b/src/backend/gporca/data/dxl/minidump/CTAS-With-Global-Local-Agg.mdp index ab8105763192..f4ba34106361 100644 --- a/src/backend/gporca/data/dxl/minidump/CTAS-With-Global-Local-Agg.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTAS-With-Global-Local-Agg.mdp @@ -710,10 +710,10 @@ - + - + @@ -727,7 +727,7 @@ - + @@ -741,7 +741,7 @@ - + @@ -752,7 +752,7 @@ - + @@ -770,7 +770,7 @@ - + @@ -781,7 +781,7 @@ - + @@ -799,7 +799,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE-4.mdp b/src/backend/gporca/data/dxl/minidump/CTE-4.mdp index 18b2a55be650..3d9d2722fd0a 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE-4.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE-4.mdp @@ -274,7 +274,7 @@ - + @@ -294,7 +294,7 @@ - + @@ -352,7 +352,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE-5.mdp b/src/backend/gporca/data/dxl/minidump/CTE-5.mdp index b4c7ba98385c..cfc937601821 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE-5.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE-5.mdp @@ -457,7 +457,7 @@ - + @@ -483,7 +483,7 @@ - + @@ -514,7 +514,7 @@ - + @@ -534,7 +534,7 @@ - + @@ -552,7 +552,7 @@ - + @@ -570,7 +570,7 @@ - + @@ -657,7 +657,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE-Join-Redistribute-Producer.mdp b/src/backend/gporca/data/dxl/minidump/CTE-Join-Redistribute-Producer.mdp index 0c9fe27c814d..a4a1309b8b04 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE-Join-Redistribute-Producer.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE-Join-Redistribute-Producer.mdp @@ -413,7 +413,7 @@ with v as (select * from t1) select * from v v1, v v2 where v1.b=v2.b; - + @@ -433,7 +433,7 @@ with v as (select * from t1) select * from v v1, v v2 where v1.b=v2.b; - + @@ -511,7 +511,7 @@ with v as (select * from t1) select * from v v1, v v2 where v1.b=v2.b; - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE-NoPushProperties.mdp b/src/backend/gporca/data/dxl/minidump/CTE-NoPushProperties.mdp index 436d6cada583..b33efdce81f7 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE-NoPushProperties.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE-NoPushProperties.mdp @@ -1110,7 +1110,7 @@ - + @@ -1136,7 +1136,7 @@ - + @@ -1244,7 +1244,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE-PushProperties.mdp b/src/backend/gporca/data/dxl/minidump/CTE-PushProperties.mdp index a8a3e0b73786..7c79a0383d5e 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE-PushProperties.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE-PushProperties.mdp @@ -1100,7 +1100,7 @@ - + @@ -1126,7 +1126,7 @@ - + @@ -1260,7 +1260,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE-volatile.mdp b/src/backend/gporca/data/dxl/minidump/CTE-volatile.mdp index e8d0a788bec2..31686aebd31c 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE-volatile.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE-volatile.mdp @@ -413,7 +413,7 @@ - + @@ -433,7 +433,7 @@ - + @@ -523,7 +523,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp index d9d3f7db9c7d..8a93dc702f64 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp @@ -419,7 +419,7 @@ - + @@ -436,7 +436,7 @@ - + @@ -516,7 +516,7 @@ - + @@ -593,7 +593,7 @@ - + @@ -609,7 +609,7 @@ - + @@ -674,7 +674,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp index 9ca88440bfb9..fe68d040693a 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp @@ -432,7 +432,7 @@ - + @@ -449,7 +449,7 @@ - + @@ -545,7 +545,7 @@ - + @@ -622,7 +622,7 @@ - + @@ -638,7 +638,7 @@ - + @@ -703,7 +703,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE25Replicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE25Replicated.mdp index b232e19e35d0..3bf623cd9df1 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE25Replicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE25Replicated.mdp @@ -409,7 +409,7 @@ - + @@ -426,7 +426,7 @@ - + @@ -514,7 +514,7 @@ - + @@ -529,7 +529,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -555,7 +555,7 @@ - + @@ -649,7 +649,7 @@ - + @@ -666,7 +666,7 @@ - + @@ -724,7 +724,7 @@ - + @@ -738,7 +738,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE2HAReplicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE2HAReplicated.mdp index 01b70c864463..a063152e4577 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE2HAReplicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE2HAReplicated.mdp @@ -388,7 +388,7 @@ - + @@ -405,7 +405,7 @@ - + @@ -485,7 +485,7 @@ - + @@ -500,7 +500,7 @@ - + @@ -509,7 +509,7 @@ - + @@ -520,13 +520,23 @@ - + + + + + + + + + + + - + @@ -602,21 +612,11 @@ - - - - - - - - - - - + @@ -700,7 +700,7 @@ - + @@ -722,7 +722,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE2Replicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE2Replicated.mdp index e57fdf24ef56..4a4166cd7359 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE2Replicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE2Replicated.mdp @@ -401,7 +401,7 @@ - + @@ -418,7 +418,7 @@ - + @@ -514,7 +514,7 @@ - + @@ -529,7 +529,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -549,13 +549,23 @@ - + + + + + + + + + + + - + @@ -631,21 +641,11 @@ - - - - - - - - - - - + @@ -729,7 +729,7 @@ - + @@ -751,7 +751,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats.mdp b/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats.mdp index 8a832e348f4d..20b72323e6db 100644 --- a/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats.mdp @@ -254,7 +254,7 @@ - + @@ -274,7 +274,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTEWithMergedGroup.mdp b/src/backend/gporca/data/dxl/minidump/CTEWithMergedGroup.mdp index d04d6fd0f91f..866cc2bce1ef 100644 --- a/src/backend/gporca/data/dxl/minidump/CTEWithMergedGroup.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTEWithMergedGroup.mdp @@ -428,7 +428,7 @@ - + @@ -439,7 +439,7 @@ - + @@ -450,7 +450,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTEinlining.mdp b/src/backend/gporca/data/dxl/minidump/CTEinlining.mdp index 3abba40eb64d..45cc33c6c235 100644 --- a/src/backend/gporca/data/dxl/minidump/CTEinlining.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTEinlining.mdp @@ -201,7 +201,7 @@ explain with v as (select x,y from bar) select v1.x from v v1, v v2 where v1.x = - + @@ -212,7 +212,7 @@ explain with v as (select x,y from bar) select v1.x from v v1, v v2 where v1.x = - + diff --git a/src/backend/gporca/data/dxl/minidump/CTG-Join.mdp b/src/backend/gporca/data/dxl/minidump/CTG-Join.mdp index 1a0fdf1e75cb..dad240cd30fb 100644 --- a/src/backend/gporca/data/dxl/minidump/CTG-Join.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTG-Join.mdp @@ -95,7 +95,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CapGbCardToSelectCard.mdp b/src/backend/gporca/data/dxl/minidump/CapGbCardToSelectCard.mdp index 5c53d2133a03..067230d417d3 100644 --- a/src/backend/gporca/data/dxl/minidump/CapGbCardToSelectCard.mdp +++ b/src/backend/gporca/data/dxl/minidump/CapGbCardToSelectCard.mdp @@ -10891,7 +10891,7 @@ group by item.i_item_id, item.i_item_desc, item.i_current_price; - + @@ -10908,7 +10908,7 @@ group by item.i_item_id, item.i_item_desc, item.i_current_price; - + @@ -10929,7 +10929,7 @@ group by item.i_item_id, item.i_item_desc, item.i_current_price; - + @@ -10957,7 +10957,7 @@ group by item.i_item_id, item.i_item_desc, item.i_current_price; - + @@ -10978,7 +10978,7 @@ group by item.i_item_id, item.i_item_desc, item.i_current_price; - + diff --git a/src/backend/gporca/data/dxl/minidump/Coalesce-With-Subquery.mdp b/src/backend/gporca/data/dxl/minidump/Coalesce-With-Subquery.mdp index 07b21608cfcb..51012d97c3b2 100644 --- a/src/backend/gporca/data/dxl/minidump/Coalesce-With-Subquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/Coalesce-With-Subquery.mdp @@ -855,7 +855,7 @@ - + @@ -923,7 +923,7 @@ - + @@ -957,7 +957,7 @@ - + @@ -990,7 +990,7 @@ - + @@ -1105,7 +1105,7 @@ - + @@ -1124,7 +1124,7 @@ - + @@ -1137,7 +1137,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CoerceToDomain.mdp b/src/backend/gporca/data/dxl/minidump/CoerceToDomain.mdp index a6ff05add566..cda4f0642141 100644 --- a/src/backend/gporca/data/dxl/minidump/CoerceToDomain.mdp +++ b/src/backend/gporca/data/dxl/minidump/CoerceToDomain.mdp @@ -1934,7 +1934,7 @@ SELECT * FROM information_schema.tables; - + @@ -2098,7 +2098,7 @@ SELECT * FROM information_schema.tables; - + @@ -2163,7 +2163,7 @@ SELECT * FROM information_schema.tables; - + diff --git a/src/backend/gporca/data/dxl/minidump/ConstTblGetUnderSubqWithNoOuterRef.mdp b/src/backend/gporca/data/dxl/minidump/ConstTblGetUnderSubqWithNoOuterRef.mdp index b5f1bc1ee31e..b8d848cb94f7 100644 --- a/src/backend/gporca/data/dxl/minidump/ConstTblGetUnderSubqWithNoOuterRef.mdp +++ b/src/backend/gporca/data/dxl/minidump/ConstTblGetUnderSubqWithNoOuterRef.mdp @@ -473,7 +473,7 @@ SELECT * FROM foo,bar WHERE a=(VALUES (1)); - + @@ -493,7 +493,7 @@ SELECT * FROM foo,bar WHERE a=(VALUES (1)); - + @@ -515,7 +515,7 @@ SELECT * FROM foo,bar WHERE a=(VALUES (1)); - + @@ -529,7 +529,7 @@ SELECT * FROM foo,bar WHERE a=(VALUES (1)); - + diff --git a/src/backend/gporca/data/dxl/minidump/ConstTblGetUnderSubqWithOuterRef.mdp b/src/backend/gporca/data/dxl/minidump/ConstTblGetUnderSubqWithOuterRef.mdp index 7cd5a3d47a88..18ccf6afa8c3 100644 --- a/src/backend/gporca/data/dxl/minidump/ConstTblGetUnderSubqWithOuterRef.mdp +++ b/src/backend/gporca/data/dxl/minidump/ConstTblGetUnderSubqWithOuterRef.mdp @@ -282,7 +282,7 @@ SELECT * FROM foo,bar WHERE a=(SELECT c); - + @@ -302,7 +302,7 @@ SELECT * FROM foo,bar WHERE a=(SELECT c); - + diff --git a/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomInsert.mdp b/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomInsert.mdp index d86fbcd370b8..78ced4cef6be 100644 --- a/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomInsert.mdp +++ b/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomInsert.mdp @@ -645,7 +645,7 @@ explain analyze insert into t1 select t2.a,t3.b from t2, t3 where t2.a = t3.a; - + @@ -667,7 +667,7 @@ explain analyze insert into t1 select t2.a,t3.b from t2, t3 where t2.a = t3.a; - + @@ -684,7 +684,7 @@ explain analyze insert into t1 select t2.a,t3.b from t2, t3 where t2.a = t3.a; - + diff --git a/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomSelect.mdp b/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomSelect.mdp index 8e31860de745..798beaf08934 100644 --- a/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomSelect.mdp +++ b/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomSelect.mdp @@ -205,7 +205,7 @@ - + @@ -225,7 +225,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CorrelatedSubqueryWithAggWindowFunc.mdp b/src/backend/gporca/data/dxl/minidump/CorrelatedSubqueryWithAggWindowFunc.mdp index e0e0d83658fb..42d0542131d9 100644 --- a/src/backend/gporca/data/dxl/minidump/CorrelatedSubqueryWithAggWindowFunc.mdp +++ b/src/backend/gporca/data/dxl/minidump/CorrelatedSubqueryWithAggWindowFunc.mdp @@ -441,7 +441,7 @@ explain select C.i from C where C.i in (select avg(i) over (partition by i) from - + @@ -452,7 +452,7 @@ explain select C.i from C where C.i in (select avg(i) over (partition by i) from - + @@ -470,7 +470,7 @@ explain select C.i from C where C.i in (select avg(i) over (partition by i) from - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-With-HJ-And-UniversalChild.mdp b/src/backend/gporca/data/dxl/minidump/DML-With-HJ-And-UniversalChild.mdp index 6fc615f2028d..a23420b746a1 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-With-HJ-And-UniversalChild.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-With-HJ-And-UniversalChild.mdp @@ -714,7 +714,7 @@ - + @@ -732,7 +732,7 @@ - + @@ -749,7 +749,7 @@ - + @@ -791,25 +791,25 @@ - + - + - + - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp b/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp index 4c092069a62c..5b2474576323 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp @@ -363,7 +363,7 @@ - + @@ -377,7 +377,7 @@ - + @@ -391,7 +391,7 @@ - + @@ -402,7 +402,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DPE-IN.mdp b/src/backend/gporca/data/dxl/minidump/DPE-IN.mdp index 250cd95d37ec..14a1d6cdd0e5 100644 --- a/src/backend/gporca/data/dxl/minidump/DPE-IN.mdp +++ b/src/backend/gporca/data/dxl/minidump/DPE-IN.mdp @@ -822,7 +822,7 @@ select * from P,X where P.a=X.a and X.a in (1,2); - + @@ -839,7 +839,7 @@ select * from P,X where P.a=X.a and X.a in (1,2); - + diff --git a/src/backend/gporca/data/dxl/minidump/DPE-NOT-IN.mdp b/src/backend/gporca/data/dxl/minidump/DPE-NOT-IN.mdp index f645acb14b3c..660b509ee6f7 100644 --- a/src/backend/gporca/data/dxl/minidump/DPE-NOT-IN.mdp +++ b/src/backend/gporca/data/dxl/minidump/DPE-NOT-IN.mdp @@ -806,7 +806,7 @@ select * from P,X where P.a=X.a and X.a not in (1,2); - + @@ -823,7 +823,7 @@ select * from P,X where P.a=X.a and X.a not in (1,2); - + diff --git a/src/backend/gporca/data/dxl/minidump/DPE-with-unsupported-pred.mdp b/src/backend/gporca/data/dxl/minidump/DPE-with-unsupported-pred.mdp index 93ae575dfbde..a4dbc7e92a92 100644 --- a/src/backend/gporca/data/dxl/minidump/DPE-with-unsupported-pred.mdp +++ b/src/backend/gporca/data/dxl/minidump/DPE-with-unsupported-pred.mdp @@ -2010,7 +2010,7 @@ - + @@ -2039,7 +2039,7 @@ - + @@ -2068,10 +2068,37 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2127,7 +2154,7 @@ - + @@ -2136,7 +2163,7 @@ - + @@ -2168,7 +2195,7 @@ - + @@ -2182,7 +2209,7 @@ - + @@ -2197,7 +2224,7 @@ - + @@ -2211,7 +2238,7 @@ - + @@ -2235,7 +2262,7 @@ - + @@ -2244,7 +2271,7 @@ - + @@ -2328,33 +2355,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/backend/gporca/data/dxl/minidump/DPv2GreedyOnly.mdp b/src/backend/gporca/data/dxl/minidump/DPv2GreedyOnly.mdp index 8a2c83685765..bcb890ab4b17 100644 --- a/src/backend/gporca/data/dxl/minidump/DPv2GreedyOnly.mdp +++ b/src/backend/gporca/data/dxl/minidump/DPv2GreedyOnly.mdp @@ -5924,7 +5924,7 @@ - + @@ -5962,7 +5962,7 @@ - + @@ -5988,7 +5988,7 @@ - + @@ -6020,7 +6020,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DPv2MinCardOnly.mdp b/src/backend/gporca/data/dxl/minidump/DPv2MinCardOnly.mdp index 94180731987c..09c737ddb219 100644 --- a/src/backend/gporca/data/dxl/minidump/DPv2MinCardOnly.mdp +++ b/src/backend/gporca/data/dxl/minidump/DPv2MinCardOnly.mdp @@ -5924,7 +5924,7 @@ - + @@ -5962,7 +5962,7 @@ - + @@ -5988,7 +5988,7 @@ - + @@ -6020,7 +6020,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DPv2QueryOnly.mdp b/src/backend/gporca/data/dxl/minidump/DPv2QueryOnly.mdp index ad9e673745ce..7099f6416960 100644 --- a/src/backend/gporca/data/dxl/minidump/DPv2QueryOnly.mdp +++ b/src/backend/gporca/data/dxl/minidump/DPv2QueryOnly.mdp @@ -5924,7 +5924,7 @@ - + @@ -5956,7 +5956,7 @@ - + @@ -5994,7 +5994,7 @@ - + @@ -6026,7 +6026,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DQA-InnerJoin-GroupBy-HashAggregate.mdp b/src/backend/gporca/data/dxl/minidump/DQA-InnerJoin-GroupBy-HashAggregate.mdp index 2a176955ff4f..f1c30deb8035 100644 --- a/src/backend/gporca/data/dxl/minidump/DQA-InnerJoin-GroupBy-HashAggregate.mdp +++ b/src/backend/gporca/data/dxl/minidump/DQA-InnerJoin-GroupBy-HashAggregate.mdp @@ -467,7 +467,7 @@ - + @@ -493,7 +493,7 @@ - + @@ -521,7 +521,7 @@ - + @@ -558,7 +558,7 @@ - + @@ -583,7 +583,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Date-TimeStamp-HashJoin.mdp b/src/backend/gporca/data/dxl/minidump/Date-TimeStamp-HashJoin.mdp index 1ba74b493678..fbc7b00e5b27 100644 --- a/src/backend/gporca/data/dxl/minidump/Date-TimeStamp-HashJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/Date-TimeStamp-HashJoin.mdp @@ -286,7 +286,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Delete-With-Limit-In-Subquery.mdp b/src/backend/gporca/data/dxl/minidump/Delete-With-Limit-In-Subquery.mdp index 7e1292b5dbb0..492c795ed794 100644 --- a/src/backend/gporca/data/dxl/minidump/Delete-With-Limit-In-Subquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/Delete-With-Limit-In-Subquery.mdp @@ -724,7 +724,7 @@ - + @@ -742,7 +742,7 @@ - + @@ -759,7 +759,7 @@ - + @@ -773,39 +773,10 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -962,6 +933,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/DeleteMismatchedDistribution.mdp b/src/backend/gporca/data/dxl/minidump/DeleteMismatchedDistribution.mdp index ea408e2ccb04..a74761faa6fb 100644 --- a/src/backend/gporca/data/dxl/minidump/DeleteMismatchedDistribution.mdp +++ b/src/backend/gporca/data/dxl/minidump/DeleteMismatchedDistribution.mdp @@ -340,7 +340,7 @@ - + @@ -360,7 +360,7 @@ - + @@ -377,7 +377,7 @@ - + @@ -391,7 +391,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTableJoin.mdp b/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTableJoin.mdp index d639a120e0bc..837de8f2ed91 100644 --- a/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTableJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTableJoin.mdp @@ -280,7 +280,7 @@ - + @@ -298,7 +298,7 @@ - + @@ -315,7 +315,7 @@ - + @@ -329,7 +329,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DisableBroadcastThreshold.mdp b/src/backend/gporca/data/dxl/minidump/DisableBroadcastThreshold.mdp index ef232c025b20..fbe2949b6b2f 100644 --- a/src/backend/gporca/data/dxl/minidump/DisableBroadcastThreshold.mdp +++ b/src/backend/gporca/data/dxl/minidump/DisableBroadcastThreshold.mdp @@ -2602,10 +2602,10 @@ - + - + @@ -2619,7 +2619,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DiscardRedistributeHashJoin.mdp b/src/backend/gporca/data/dxl/minidump/DiscardRedistributeHashJoin.mdp index 3b52b4232c67..314c53cb74ff 100644 --- a/src/backend/gporca/data/dxl/minidump/DiscardRedistributeHashJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/DiscardRedistributeHashJoin.mdp @@ -291,7 +291,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DynamicIndexGet-OuterRefs.mdp b/src/backend/gporca/data/dxl/minidump/DynamicIndexGet-OuterRefs.mdp index 924b4463dc19..62bdbcb40795 100644 --- a/src/backend/gporca/data/dxl/minidump/DynamicIndexGet-OuterRefs.mdp +++ b/src/backend/gporca/data/dxl/minidump/DynamicIndexGet-OuterRefs.mdp @@ -1301,7 +1301,7 @@ - + @@ -1312,7 +1312,7 @@ - + @@ -1361,7 +1361,7 @@ - + @@ -1382,7 +1382,7 @@ - + @@ -1405,7 +1405,7 @@ - + @@ -1443,7 +1443,7 @@ - + @@ -1466,7 +1466,7 @@ - + @@ -1483,7 +1483,7 @@ - + @@ -1502,7 +1502,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EagerAggEmptyInput.mdp b/src/backend/gporca/data/dxl/minidump/EagerAggEmptyInput.mdp index 6dc17a937936..e2d7a976be9e 100644 --- a/src/backend/gporca/data/dxl/minidump/EagerAggEmptyInput.mdp +++ b/src/backend/gporca/data/dxl/minidump/EagerAggEmptyInput.mdp @@ -336,7 +336,7 @@ - + @@ -356,7 +356,7 @@ - + @@ -383,7 +383,7 @@ - + @@ -405,7 +405,7 @@ - + @@ -430,7 +430,7 @@ - + @@ -447,7 +447,7 @@ - + @@ -474,7 +474,7 @@ - + @@ -496,7 +496,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EagerAggExpression.mdp b/src/backend/gporca/data/dxl/minidump/EagerAggExpression.mdp index bedfbf5a9c0d..2859094717d4 100644 --- a/src/backend/gporca/data/dxl/minidump/EagerAggExpression.mdp +++ b/src/backend/gporca/data/dxl/minidump/EagerAggExpression.mdp @@ -3330,7 +3330,7 @@ - + @@ -3341,7 +3341,7 @@ - + @@ -3352,7 +3352,7 @@ - + @@ -3375,7 +3375,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EagerAggGroupColumnInJoin.mdp b/src/backend/gporca/data/dxl/minidump/EagerAggGroupColumnInJoin.mdp index 0f6647f3f48d..d2fb5ae9c818 100644 --- a/src/backend/gporca/data/dxl/minidump/EagerAggGroupColumnInJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/EagerAggGroupColumnInJoin.mdp @@ -332,7 +332,7 @@ - + @@ -349,7 +349,7 @@ - + @@ -376,7 +376,7 @@ - + @@ -398,7 +398,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EagerAggMax.mdp b/src/backend/gporca/data/dxl/minidump/EagerAggMax.mdp index f1c941a62956..77afdb1bebfc 100644 --- a/src/backend/gporca/data/dxl/minidump/EagerAggMax.mdp +++ b/src/backend/gporca/data/dxl/minidump/EagerAggMax.mdp @@ -335,7 +335,7 @@ - + @@ -355,7 +355,7 @@ - + @@ -382,7 +382,7 @@ - + @@ -404,7 +404,7 @@ - + @@ -429,7 +429,7 @@ - + @@ -446,7 +446,7 @@ - + @@ -473,7 +473,7 @@ - + @@ -495,7 +495,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EagerAggMaxWithNestedLoop.mdp b/src/backend/gporca/data/dxl/minidump/EagerAggMaxWithNestedLoop.mdp index c208249d4fa0..260b36cce4c0 100644 --- a/src/backend/gporca/data/dxl/minidump/EagerAggMaxWithNestedLoop.mdp +++ b/src/backend/gporca/data/dxl/minidump/EagerAggMaxWithNestedLoop.mdp @@ -4295,10 +4295,10 @@ - + - + @@ -4318,7 +4318,7 @@ - + @@ -4332,9 +4332,9 @@ - + - + @@ -4345,7 +4345,7 @@ - + @@ -4367,7 +4367,7 @@ - + @@ -4392,7 +4392,7 @@ - + @@ -4409,7 +4409,7 @@ - + @@ -4417,9 +4417,9 @@ - + - + @@ -4436,7 +4436,7 @@ - + @@ -4453,49 +4453,10 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4571,6 +4532,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/EagerAggMinMax.mdp b/src/backend/gporca/data/dxl/minidump/EagerAggMinMax.mdp index 1195be500b0b..00dae3b82f0b 100644 --- a/src/backend/gporca/data/dxl/minidump/EagerAggMinMax.mdp +++ b/src/backend/gporca/data/dxl/minidump/EagerAggMinMax.mdp @@ -3337,7 +3337,7 @@ - + @@ -3351,7 +3351,7 @@ - + @@ -3365,7 +3365,7 @@ - + @@ -3398,7 +3398,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EagerAggSubquery.mdp b/src/backend/gporca/data/dxl/minidump/EagerAggSubquery.mdp index cc49f23a7aea..4f7ff27f129f 100644 --- a/src/backend/gporca/data/dxl/minidump/EagerAggSubquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/EagerAggSubquery.mdp @@ -381,7 +381,7 @@ - + @@ -398,7 +398,7 @@ - + @@ -425,7 +425,7 @@ - + @@ -447,7 +447,7 @@ - + @@ -472,7 +472,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EagerAggUnsupportedAgg.mdp b/src/backend/gporca/data/dxl/minidump/EagerAggUnsupportedAgg.mdp index 0a80e9a1d7fe..94e56c962479 100644 --- a/src/backend/gporca/data/dxl/minidump/EagerAggUnsupportedAgg.mdp +++ b/src/backend/gporca/data/dxl/minidump/EagerAggUnsupportedAgg.mdp @@ -3373,7 +3373,7 @@ - + @@ -3393,7 +3393,7 @@ - + @@ -3436,7 +3436,7 @@ - + @@ -3461,7 +3461,7 @@ - + @@ -3481,7 +3481,7 @@ - + @@ -3529,7 +3529,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin.mdp b/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin.mdp index 82090c324cf1..448e96ca8e1c 100644 --- a/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin.mdp @@ -533,7 +533,7 @@ - + @@ -553,7 +553,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin2.mdp b/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin2.mdp index 22a3dca3f5eb..6dfd22eff94c 100644 --- a/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin2.mdp +++ b/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin2.mdp @@ -10287,7 +10287,7 @@ - + @@ -10355,7 +10355,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin3.mdp b/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin3.mdp index c90cfcff930d..338fc13839d2 100644 --- a/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin3.mdp +++ b/src/backend/gporca/data/dxl/minidump/EffectOfLocalPredOnJoin3.mdp @@ -13134,7 +13134,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EffectsOfJoinFilter.mdp b/src/backend/gporca/data/dxl/minidump/EffectsOfJoinFilter.mdp index 19ac993efc17..06f017598d56 100644 --- a/src/backend/gporca/data/dxl/minidump/EffectsOfJoinFilter.mdp +++ b/src/backend/gporca/data/dxl/minidump/EffectsOfJoinFilter.mdp @@ -445,7 +445,7 @@ - + @@ -465,7 +465,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EqualityJoin.mdp b/src/backend/gporca/data/dxl/minidump/EqualityJoin.mdp index 89175d6f67e3..e782f8eef783 100644 --- a/src/backend/gporca/data/dxl/minidump/EqualityJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/EqualityJoin.mdp @@ -421,7 +421,7 @@ - + @@ -441,7 +441,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EquiJoinOnExpr-Supported.mdp b/src/backend/gporca/data/dxl/minidump/EquiJoinOnExpr-Supported.mdp index 2e1385e3633a..624810c7fedb 100644 --- a/src/backend/gporca/data/dxl/minidump/EquiJoinOnExpr-Supported.mdp +++ b/src/backend/gporca/data/dxl/minidump/EquiJoinOnExpr-Supported.mdp @@ -1241,7 +1241,7 @@ - + @@ -1303,7 +1303,7 @@ - + @@ -1378,7 +1378,7 @@ - + @@ -1441,7 +1441,7 @@ - + @@ -1505,7 +1505,7 @@ - + @@ -1556,7 +1556,7 @@ - + @@ -1613,7 +1613,7 @@ - + @@ -1658,7 +1658,7 @@ - + @@ -1691,7 +1691,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EquiJoinOnExpr-Unsupported.mdp b/src/backend/gporca/data/dxl/minidump/EquiJoinOnExpr-Unsupported.mdp index 9888d5ea0fb5..2f602914c797 100644 --- a/src/backend/gporca/data/dxl/minidump/EquiJoinOnExpr-Unsupported.mdp +++ b/src/backend/gporca/data/dxl/minidump/EquiJoinOnExpr-Unsupported.mdp @@ -1179,7 +1179,7 @@ - + @@ -1244,7 +1244,7 @@ - + @@ -1444,7 +1444,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Equiv-HashedDistr-1.mdp b/src/backend/gporca/data/dxl/minidump/Equiv-HashedDistr-1.mdp index 6ab86c1b281d..a0a3b7346a89 100644 --- a/src/backend/gporca/data/dxl/minidump/Equiv-HashedDistr-1.mdp +++ b/src/backend/gporca/data/dxl/minidump/Equiv-HashedDistr-1.mdp @@ -1937,7 +1937,7 @@ - + @@ -1948,7 +1948,7 @@ - + @@ -1957,7 +1957,7 @@ - + @@ -1966,7 +1966,7 @@ - + @@ -1979,7 +1979,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Equiv-HashedDistr-2.mdp b/src/backend/gporca/data/dxl/minidump/Equiv-HashedDistr-2.mdp index 81e16b4dc0dd..3c78762b11bc 100644 --- a/src/backend/gporca/data/dxl/minidump/Equiv-HashedDistr-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/Equiv-HashedDistr-2.mdp @@ -1939,7 +1939,7 @@ - + @@ -1953,7 +1953,7 @@ - + @@ -1965,7 +1965,7 @@ - + @@ -1974,7 +1974,7 @@ - + @@ -1987,7 +1987,7 @@ - + @@ -2089,7 +2089,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EquivClassesAndOr.mdp b/src/backend/gporca/data/dxl/minidump/EquivClassesAndOr.mdp index c6af5632172e..0708e3cf185a 100644 --- a/src/backend/gporca/data/dxl/minidump/EquivClassesAndOr.mdp +++ b/src/backend/gporca/data/dxl/minidump/EquivClassesAndOr.mdp @@ -347,7 +347,7 @@ - + @@ -382,7 +382,7 @@ - + @@ -423,7 +423,7 @@ - + @@ -454,7 +454,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/EquivClassesIntersect.mdp b/src/backend/gporca/data/dxl/minidump/EquivClassesIntersect.mdp index 9a1a74e61f2d..63c769b17c8c 100644 --- a/src/backend/gporca/data/dxl/minidump/EquivClassesIntersect.mdp +++ b/src/backend/gporca/data/dxl/minidump/EquivClassesIntersect.mdp @@ -298,7 +298,7 @@ - + @@ -309,7 +309,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Equivalence-class-project-over-LOJ.mdp b/src/backend/gporca/data/dxl/minidump/Equivalence-class-project-over-LOJ.mdp index 8010a11dfa9f..6907e80b36f6 100644 --- a/src/backend/gporca/data/dxl/minidump/Equivalence-class-project-over-LOJ.mdp +++ b/src/backend/gporca/data/dxl/minidump/Equivalence-class-project-over-LOJ.mdp @@ -379,7 +379,7 @@ - + @@ -397,7 +397,7 @@ - + @@ -408,7 +408,7 @@ - + @@ -424,20 +424,20 @@ - + - + - + diff --git a/src/backend/gporca/data/dxl/minidump/EstimateJoinRowsForCastPredicates.mdp b/src/backend/gporca/data/dxl/minidump/EstimateJoinRowsForCastPredicates.mdp index 65991b52b0a2..316419add787 100644 --- a/src/backend/gporca/data/dxl/minidump/EstimateJoinRowsForCastPredicates.mdp +++ b/src/backend/gporca/data/dxl/minidump/EstimateJoinRowsForCastPredicates.mdp @@ -518,7 +518,7 @@ explain select * from A,B where A.j=B.j; - + diff --git a/src/backend/gporca/data/dxl/minidump/Except-Volatile-Func.mdp b/src/backend/gporca/data/dxl/minidump/Except-Volatile-Func.mdp index 895d06aaebc2..3adb73a984d3 100644 --- a/src/backend/gporca/data/dxl/minidump/Except-Volatile-Func.mdp +++ b/src/backend/gporca/data/dxl/minidump/Except-Volatile-Func.mdp @@ -112,7 +112,7 @@ - + @@ -125,7 +125,7 @@ - + @@ -140,7 +140,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Except.mdp b/src/backend/gporca/data/dxl/minidump/Except.mdp index fe6ebd6d32ef..5e5f40efa86f 100644 --- a/src/backend/gporca/data/dxl/minidump/Except.mdp +++ b/src/backend/gporca/data/dxl/minidump/Except.mdp @@ -321,7 +321,7 @@ - + @@ -332,7 +332,7 @@ - + @@ -345,7 +345,7 @@ - + @@ -360,7 +360,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ExceptAllCompatibleDataType.mdp b/src/backend/gporca/data/dxl/minidump/ExceptAllCompatibleDataType.mdp index 5ca667abe343..cbd19f8b4591 100644 --- a/src/backend/gporca/data/dxl/minidump/ExceptAllCompatibleDataType.mdp +++ b/src/backend/gporca/data/dxl/minidump/ExceptAllCompatibleDataType.mdp @@ -416,7 +416,7 @@ - + @@ -430,7 +430,7 @@ - + @@ -464,7 +464,7 @@ - + @@ -489,7 +489,7 @@ - + @@ -506,7 +506,7 @@ - + @@ -522,7 +522,7 @@ - + @@ -541,7 +541,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ExceptHashCompatibleRange.mdp b/src/backend/gporca/data/dxl/minidump/ExceptHashCompatibleRange.mdp index 778cdc59e553..ff5080ab8ff6 100644 --- a/src/backend/gporca/data/dxl/minidump/ExceptHashCompatibleRange.mdp +++ b/src/backend/gporca/data/dxl/minidump/ExceptHashCompatibleRange.mdp @@ -150,7 +150,7 @@ - + @@ -163,7 +163,7 @@ - + @@ -178,7 +178,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin.mdp b/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin.mdp index 2d99d8ed9984..55354272d4c3 100644 --- a/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin.mdp @@ -224,7 +224,7 @@ select * from t full join s on t1 = s1; - + @@ -238,7 +238,7 @@ select * from t full join s on t1 = s1; - + @@ -252,7 +252,7 @@ select * from t full join s on t1 = s1; - + @@ -347,7 +347,7 @@ select * from t full join s on t1 = s1; - + @@ -442,7 +442,7 @@ select * from t full join s on t1 = s1; - + @@ -455,7 +455,7 @@ select * from t full join s on t1 = s1; - + @@ -544,7 +544,7 @@ select * from t full join s on t1 = s1; - + @@ -558,7 +558,7 @@ select * from t full join s on t1 = s1; - + diff --git a/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin2.mdp b/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin2.mdp index 1124f6f06863..9e0a68066fdb 100644 --- a/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin2.mdp +++ b/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin2.mdp @@ -206,7 +206,7 @@ select * from s full join s s1 on s.a = s1.a where s.a is null; - + @@ -220,7 +220,7 @@ select * from s full join s s1 on s.a = s1.a where s.a is null; - + @@ -238,7 +238,7 @@ select * from s full join s s1 on s.a = s1.a where s.a is null; - + @@ -325,7 +325,7 @@ select * from s full join s s1 on s.a = s1.a where s.a is null; - + @@ -412,7 +412,7 @@ select * from s full join s s1 on s.a = s1.a where s.a is null; - + @@ -425,7 +425,7 @@ select * from s full join s s1 on s.a = s1.a where s.a is null; - + @@ -508,7 +508,7 @@ select * from s full join s s1 on s.a = s1.a where s.a is null; - + @@ -522,7 +522,7 @@ select * from s full join s s1 on s.a = s1.a where s.a is null; - + diff --git a/src/backend/gporca/data/dxl/minidump/ExpandJoinOrder.mdp b/src/backend/gporca/data/dxl/minidump/ExpandJoinOrder.mdp index 10e17cadbdd9..ccc12733d65c 100644 --- a/src/backend/gporca/data/dxl/minidump/ExpandJoinOrder.mdp +++ b/src/backend/gporca/data/dxl/minidump/ExpandJoinOrder.mdp @@ -868,7 +868,7 @@ - + @@ -970,7 +970,7 @@ - + @@ -1107,7 +1107,7 @@ - + @@ -1209,7 +1209,7 @@ - + @@ -1348,7 +1348,7 @@ - + @@ -1516,7 +1516,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ExpandNAryJoinGreedyWithLOJOnly.mdp b/src/backend/gporca/data/dxl/minidump/ExpandNAryJoinGreedyWithLOJOnly.mdp index f115f5dc540d..abcafd950b1f 100644 --- a/src/backend/gporca/data/dxl/minidump/ExpandNAryJoinGreedyWithLOJOnly.mdp +++ b/src/backend/gporca/data/dxl/minidump/ExpandNAryJoinGreedyWithLOJOnly.mdp @@ -3937,7 +3937,7 @@ - + @@ -3960,7 +3960,7 @@ - + @@ -3989,7 +3989,7 @@ - + @@ -4020,7 +4020,7 @@ - + @@ -4083,7 +4083,7 @@ - + @@ -4105,7 +4105,7 @@ - + @@ -4169,7 +4169,7 @@ - + @@ -4194,7 +4194,7 @@ - + @@ -4247,7 +4247,7 @@ - + @@ -4266,7 +4266,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ExtractPredicateFromDisj.mdp b/src/backend/gporca/data/dxl/minidump/ExtractPredicateFromDisj.mdp index 66206122f4ca..0e2baf0b7bd0 100644 --- a/src/backend/gporca/data/dxl/minidump/ExtractPredicateFromDisj.mdp +++ b/src/backend/gporca/data/dxl/minidump/ExtractPredicateFromDisj.mdp @@ -14565,7 +14565,7 @@ WHERE ((sale_type = 's'::text AND dyear = 2001 AND year_total > 0::numeric) O - + @@ -14603,7 +14603,7 @@ WHERE ((sale_type = 's'::text AND dyear = 2001 AND year_total > 0::numeric) O - + @@ -14696,7 +14696,7 @@ WHERE ((sale_type = 's'::text AND dyear = 2001 AND year_total > 0::numeric) O - + @@ -14737,7 +14737,7 @@ WHERE ((sale_type = 's'::text AND dyear = 2001 AND year_total > 0::numeric) O - + @@ -14795,7 +14795,7 @@ WHERE ((sale_type = 's'::text AND dyear = 2001 AND year_total > 0::numeric) O - + @@ -14842,7 +14842,7 @@ WHERE ((sale_type = 's'::text AND dyear = 2001 AND year_total > 0::numeric) O - + @@ -14873,7 +14873,7 @@ WHERE ((sale_type = 's'::text AND dyear = 2001 AND year_total > 0::numeric) O - + diff --git a/src/backend/gporca/data/dxl/minidump/ExtractPredicateFromDisjWithComputedColumns.mdp b/src/backend/gporca/data/dxl/minidump/ExtractPredicateFromDisjWithComputedColumns.mdp index 51122f17d2ca..54dc106cf0ea 100644 --- a/src/backend/gporca/data/dxl/minidump/ExtractPredicateFromDisjWithComputedColumns.mdp +++ b/src/backend/gporca/data/dxl/minidump/ExtractPredicateFromDisjWithComputedColumns.mdp @@ -462,7 +462,7 @@ where (cd.dyear = 2001 and s.tickets_cnt > 3) or (cd.dmoy = 4 and s.tickets_c - + @@ -497,7 +497,7 @@ where (cd.dyear = 2001 and s.tickets_cnt > 3) or (cd.dmoy = 4 and s.tickets_c - + @@ -529,49 +529,26 @@ where (cd.dyear = 2001 and s.tickets_cnt > 3) or (cd.dmoy = 4 and s.tickets_c - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - + - - + + - - + + - - + + @@ -587,22 +564,22 @@ where (cd.dyear = 2001 and s.tickets_cnt > 3) or (cd.dmoy = 4 and s.tickets_c - + - + - - + + - - + + - - + + @@ -615,41 +592,75 @@ where (cd.dyear = 2001 and s.tickets_cnt > 3) or (cd.dmoy = 4 and s.tickets_c - + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + - - + + - - + + - - + + - - + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + @@ -672,7 +683,7 @@ where (cd.dyear = 2001 and s.tickets_cnt > 3) or (cd.dmoy = 4 and s.tickets_c - + @@ -713,9 +724,9 @@ where (cd.dyear = 2001 and s.tickets_cnt > 3) or (cd.dmoy = 4 and s.tickets_c - + - + @@ -804,9 +815,9 @@ where (cd.dyear = 2001 and s.tickets_cnt > 3) or (cd.dmoy = 4 and s.tickets_c - + - + @@ -887,43 +898,32 @@ where (cd.dyear = 2001 and s.tickets_cnt > 3) or (cd.dmoy = 4 and s.tickets_c - + - - + + - - + + - - + + - - - - - - - - - - - - - + + - - - - - - - - - - + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/Factorized-Preds.mdp b/src/backend/gporca/data/dxl/minidump/Factorized-Preds.mdp index 93d4eabf9230..3c38393f9f22 100644 --- a/src/backend/gporca/data/dxl/minidump/Factorized-Preds.mdp +++ b/src/backend/gporca/data/dxl/minidump/Factorized-Preds.mdp @@ -278,7 +278,7 @@ - + @@ -298,7 +298,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/FullJoin-NonDefaultOpfamily.mdp b/src/backend/gporca/data/dxl/minidump/FullJoin-NonDefaultOpfamily.mdp index 1f5cf56d1589..f90f7d9adee6 100644 --- a/src/backend/gporca/data/dxl/minidump/FullJoin-NonDefaultOpfamily.mdp +++ b/src/backend/gporca/data/dxl/minidump/FullJoin-NonDefaultOpfamily.mdp @@ -352,7 +352,7 @@ - + @@ -366,7 +366,7 @@ - + @@ -453,7 +453,7 @@ - + @@ -540,7 +540,7 @@ - + @@ -553,7 +553,7 @@ - + @@ -694,7 +694,7 @@ - + @@ -708,7 +708,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/FullJoin-Subquery-CastedPredicates.mdp b/src/backend/gporca/data/dxl/minidump/FullJoin-Subquery-CastedPredicates.mdp index cdffe31e27dd..355f7f5f18b2 100644 --- a/src/backend/gporca/data/dxl/minidump/FullJoin-Subquery-CastedPredicates.mdp +++ b/src/backend/gporca/data/dxl/minidump/FullJoin-Subquery-CastedPredicates.mdp @@ -490,7 +490,7 @@ - + @@ -513,7 +513,7 @@ - + @@ -654,7 +654,7 @@ - + @@ -678,7 +678,7 @@ - + @@ -703,7 +703,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/FullOuterJoin-NullRejectingLHS1.mdp b/src/backend/gporca/data/dxl/minidump/FullOuterJoin-NullRejectingLHS1.mdp index 595aea9b962b..9696295fc1d9 100644 --- a/src/backend/gporca/data/dxl/minidump/FullOuterJoin-NullRejectingLHS1.mdp +++ b/src/backend/gporca/data/dxl/minidump/FullOuterJoin-NullRejectingLHS1.mdp @@ -264,7 +264,7 @@ - + @@ -284,7 +284,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/FullOuterJoin-NullRejectingLHS2.mdp b/src/backend/gporca/data/dxl/minidump/FullOuterJoin-NullRejectingLHS2.mdp index 8ec9d9a28d59..ec2fa7ccbc87 100644 --- a/src/backend/gporca/data/dxl/minidump/FullOuterJoin-NullRejectingLHS2.mdp +++ b/src/backend/gporca/data/dxl/minidump/FullOuterJoin-NullRejectingLHS2.mdp @@ -336,7 +336,7 @@ - + @@ -347,7 +347,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/GreedyNAryDelayCrossJoin.mdp b/src/backend/gporca/data/dxl/minidump/GreedyNAryDelayCrossJoin.mdp index bce7e4afc819..08d2d089905b 100644 --- a/src/backend/gporca/data/dxl/minidump/GreedyNAryDelayCrossJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/GreedyNAryDelayCrossJoin.mdp @@ -1280,7 +1280,7 @@ EXPLAIN SELECT * FROM t1, t2, t3, t4 where b = c; - + @@ -1300,7 +1300,7 @@ EXPLAIN SELECT * FROM t1, t2, t3, t4 where b = c; - + @@ -1322,7 +1322,7 @@ EXPLAIN SELECT * FROM t1, t2, t3, t4 where b = c; - + @@ -1341,7 +1341,7 @@ EXPLAIN SELECT * FROM t1, t2, t3, t4 where b = c; - + diff --git a/src/backend/gporca/data/dxl/minidump/GreedyNAryJoin.mdp b/src/backend/gporca/data/dxl/minidump/GreedyNAryJoin.mdp index 71636d0afc41..61b30c16e1f5 100644 --- a/src/backend/gporca/data/dxl/minidump/GreedyNAryJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/GreedyNAryJoin.mdp @@ -1144,10 +1144,10 @@ EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON a =b INNER JOIN t3 ON b = c; - + - + @@ -1164,7 +1164,7 @@ EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON a =b INNER JOIN t3 ON b = c; - + @@ -1181,53 +1181,53 @@ EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON a =b INNER JOIN t3 ON b = c; + - - + - + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - @@ -1251,30 +1251,30 @@ EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON a =b INNER JOIN t3 ON b = c; + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/backend/gporca/data/dxl/minidump/GreedyNAryJoinWithDisconnectedEdges.mdp b/src/backend/gporca/data/dxl/minidump/GreedyNAryJoinWithDisconnectedEdges.mdp index 57490d5fd937..1f70e88d0e89 100644 --- a/src/backend/gporca/data/dxl/minidump/GreedyNAryJoinWithDisconnectedEdges.mdp +++ b/src/backend/gporca/data/dxl/minidump/GreedyNAryJoinWithDisconnectedEdges.mdp @@ -3765,7 +3765,7 @@ - + @@ -3821,7 +3821,7 @@ - + @@ -3879,7 +3879,7 @@ - + @@ -3931,7 +3931,7 @@ - + @@ -3981,7 +3981,7 @@ - + @@ -4021,7 +4021,7 @@ - + @@ -4059,7 +4059,7 @@ - + @@ -4087,7 +4087,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/GroupByOuterRef.mdp b/src/backend/gporca/data/dxl/minidump/GroupByOuterRef.mdp index 30b8e4b2de45..eaa2b3e350a2 100644 --- a/src/backend/gporca/data/dxl/minidump/GroupByOuterRef.mdp +++ b/src/backend/gporca/data/dxl/minidump/GroupByOuterRef.mdp @@ -328,7 +328,7 @@ where t.a in (select count(s.i) from s); - + diff --git a/src/backend/gporca/data/dxl/minidump/GroupingOnSameTblCol-2.mdp b/src/backend/gporca/data/dxl/minidump/GroupingOnSameTblCol-2.mdp index 073663571b27..2b1048201f7f 100644 --- a/src/backend/gporca/data/dxl/minidump/GroupingOnSameTblCol-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/GroupingOnSameTblCol-2.mdp @@ -4117,7 +4117,7 @@ - + @@ -4128,21 +4128,21 @@ - + - + - + @@ -4175,7 +4175,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/HAWQ-TPCH-Stat-Derivation.mdp b/src/backend/gporca/data/dxl/minidump/HAWQ-TPCH-Stat-Derivation.mdp index 1b1c27ad308b..677bae97123d 100644 --- a/src/backend/gporca/data/dxl/minidump/HAWQ-TPCH-Stat-Derivation.mdp +++ b/src/backend/gporca/data/dxl/minidump/HAWQ-TPCH-Stat-Derivation.mdp @@ -6379,10 +6379,10 @@ ORDER BY s_name; - + - + @@ -6398,7 +6398,7 @@ ORDER BY s_name; - + @@ -6416,7 +6416,7 @@ ORDER BY s_name; - + @@ -6436,7 +6436,7 @@ ORDER BY s_name; - + @@ -6458,7 +6458,7 @@ ORDER BY s_name; - + @@ -6512,7 +6512,7 @@ ORDER BY s_name; - + @@ -6528,7 +6528,7 @@ ORDER BY s_name; - + @@ -6546,229 +6546,128 @@ ORDER BY s_name; - + - + - + - - + + - - + + - - + + - - - - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + + - + + + + + + + - - - - - - - - - - - - - + + - + + + + + + + + + + + + + + + - - - - - + - + - - - - - - - - - - - - - - + + + - + + + + + + + + + + - + @@ -6777,34 +6676,150 @@ ORDER BY s_name; - - + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/HJN-DPE-Bitmap-Outer-Child.mdp b/src/backend/gporca/data/dxl/minidump/HJN-DPE-Bitmap-Outer-Child.mdp index da79ae41674d..1c244ae8eb1a 100644 --- a/src/backend/gporca/data/dxl/minidump/HJN-DPE-Bitmap-Outer-Child.mdp +++ b/src/backend/gporca/data/dxl/minidump/HJN-DPE-Bitmap-Outer-Child.mdp @@ -828,7 +828,7 @@ select * from dbs_target, dbs_helper where dbs_target.c1 = dbs_helper.c1 and dbs - + @@ -854,7 +854,7 @@ select * from dbs_target, dbs_helper where dbs_target.c1 = dbs_helper.c1 and dbs - + diff --git a/src/backend/gporca/data/dxl/minidump/HJN-DeeperOuter.mdp b/src/backend/gporca/data/dxl/minidump/HJN-DeeperOuter.mdp index 816ed4d36510..0bb128831a21 100644 --- a/src/backend/gporca/data/dxl/minidump/HJN-DeeperOuter.mdp +++ b/src/backend/gporca/data/dxl/minidump/HJN-DeeperOuter.mdp @@ -359,7 +359,7 @@ SELECT z.*,x.* FROM x,y,z WHERE x.i=y.i AND x.i=z.i; - + @@ -382,7 +382,7 @@ SELECT z.*,x.* FROM x,y,z WHERE x.i=y.i AND x.i=z.i; - + @@ -444,7 +444,7 @@ SELECT z.*,x.* FROM x,y,z WHERE x.i=y.i AND x.i=z.i; - + @@ -468,7 +468,7 @@ SELECT z.*,x.* FROM x,y,z WHERE x.i=y.i AND x.i=z.i; - + diff --git a/src/backend/gporca/data/dxl/minidump/HJN-Redistribute-One-Side.mdp b/src/backend/gporca/data/dxl/minidump/HJN-Redistribute-One-Side.mdp index e512f35217e5..ad5f2bd9526a 100644 --- a/src/backend/gporca/data/dxl/minidump/HJN-Redistribute-One-Side.mdp +++ b/src/backend/gporca/data/dxl/minidump/HJN-Redistribute-One-Side.mdp @@ -4554,7 +4554,7 @@ - + @@ -4637,7 +4637,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/HashJoinOnRelabeledColumns.mdp b/src/backend/gporca/data/dxl/minidump/HashJoinOnRelabeledColumns.mdp index b867dca16756..1afd66e01001 100644 --- a/src/backend/gporca/data/dxl/minidump/HashJoinOnRelabeledColumns.mdp +++ b/src/backend/gporca/data/dxl/minidump/HashJoinOnRelabeledColumns.mdp @@ -268,7 +268,7 @@ - + @@ -282,7 +282,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/IN-ArrayCmp.mdp b/src/backend/gporca/data/dxl/minidump/IN-ArrayCmp.mdp index f4076348ca59..bf6558140698 100644 --- a/src/backend/gporca/data/dxl/minidump/IN-ArrayCmp.mdp +++ b/src/backend/gporca/data/dxl/minidump/IN-ArrayCmp.mdp @@ -254,7 +254,7 @@ - + @@ -274,7 +274,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InSubqueryWithNotNullCol.mdp b/src/backend/gporca/data/dxl/minidump/InSubqueryWithNotNullCol.mdp index 7b1b33fa5fcc..306a5c187932 100644 --- a/src/backend/gporca/data/dxl/minidump/InSubqueryWithNotNullCol.mdp +++ b/src/backend/gporca/data/dxl/minidump/InSubqueryWithNotNullCol.mdp @@ -426,7 +426,7 @@ select * - + @@ -440,7 +440,7 @@ select * - + @@ -489,7 +489,7 @@ select * - + @@ -534,7 +534,7 @@ select * - + @@ -573,7 +573,7 @@ select * - + @@ -606,7 +606,7 @@ select * - + diff --git a/src/backend/gporca/data/dxl/minidump/IndexApply-ForPartialIndex.mdp b/src/backend/gporca/data/dxl/minidump/IndexApply-ForPartialIndex.mdp index 7992d82d8789..f768f1ff16a7 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexApply-ForPartialIndex.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexApply-ForPartialIndex.mdp @@ -390,7 +390,7 @@ explain select t1.a from t1, foo_prt t2, t3 where (t1.b = t2.a) and t2.a = t3.b; - + @@ -401,7 +401,7 @@ explain select t1.a from t1, foo_prt t2, t3 where (t1.b = t2.a) and t2.a = t3.b; - + @@ -465,7 +465,7 @@ explain select t1.a from t1, foo_prt t2, t3 where (t1.b = t2.a) and t2.a = t3.b; - + diff --git a/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondDisjointWithHashedDistr.mdp b/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondDisjointWithHashedDistr.mdp index 98017cc41251..5513be15cedc 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondDisjointWithHashedDistr.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondDisjointWithHashedDistr.mdp @@ -827,7 +827,7 @@ - + @@ -850,7 +850,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondIntersectWithHashedDistr.mdp b/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondIntersectWithHashedDistr.mdp index 89a2813898aa..dd3102970530 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondIntersectWithHashedDistr.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondIntersectWithHashedDistr.mdp @@ -833,7 +833,7 @@ - + @@ -856,7 +856,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondMatchHashedDistr.mdp b/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondMatchHashedDistr.mdp index c05a408cb562..19a475821ffc 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondMatchHashedDistr.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondMatchHashedDistr.mdp @@ -805,7 +805,7 @@ - + @@ -825,7 +825,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondSubsetOfHashedDistr.mdp b/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondSubsetOfHashedDistr.mdp index 98017cc41251..5513be15cedc 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondSubsetOfHashedDistr.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondSubsetOfHashedDistr.mdp @@ -827,7 +827,7 @@ - + @@ -850,7 +850,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondSupersetOfHashedDistr.mdp b/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondSupersetOfHashedDistr.mdp index 292aefe624e2..5e46dd96112b 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondSupersetOfHashedDistr.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexApply-IndexCondSupersetOfHashedDistr.mdp @@ -819,7 +819,7 @@ - + @@ -839,7 +839,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/IndexApply-InnerSelect-PartTable2.mdp b/src/backend/gporca/data/dxl/minidump/IndexApply-InnerSelect-PartTable2.mdp index 71aad8aff3cd..9d30106a6798 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexApply-InnerSelect-PartTable2.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexApply-InnerSelect-PartTable2.mdp @@ -406,7 +406,7 @@ select * from bar, (select * from foo where foo.a in (select h.a from bar h, bar - + @@ -426,7 +426,7 @@ select * from bar, (select * from foo where foo.a in (select h.a from bar h, bar - + diff --git a/src/backend/gporca/data/dxl/minidump/IndexGet-OuterRefs.mdp b/src/backend/gporca/data/dxl/minidump/IndexGet-OuterRefs.mdp index e415f19da763..2c0c7a671b42 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexGet-OuterRefs.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexGet-OuterRefs.mdp @@ -263,7 +263,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/IndexScanWithNestedCTEAndSetOp.mdp b/src/backend/gporca/data/dxl/minidump/IndexScanWithNestedCTEAndSetOp.mdp index a82aa41348eb..0655e7768952 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexScanWithNestedCTEAndSetOp.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexScanWithNestedCTEAndSetOp.mdp @@ -353,7 +353,7 @@ SELECT a FROM y WHERE (a IN (SELECT b FROM bar WHERE b = 2)); - + @@ -364,7 +364,7 @@ SELECT a FROM y WHERE (a IN (SELECT b FROM bar WHERE b = 2)); - + @@ -375,7 +375,7 @@ SELECT a FROM y WHERE (a IN (SELECT b FROM bar WHERE b = 2)); - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicates.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicates.mdp index 34606cff1bc5..19e1fb8a6fbc 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicates.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicates.mdp @@ -346,7 +346,7 @@ explain select 1+row_number() over(partition by foo.c) from foo, bar where foo.a - + @@ -357,7 +357,7 @@ explain select 1+row_number() over(partition by foo.c) from foo, bar where foo.a - + @@ -371,7 +371,7 @@ explain select 1+row_number() over(partition by foo.c) from foo, bar where foo.a - + @@ -384,7 +384,7 @@ explain select 1+row_number() over(partition by foo.c) from foo, bar where foo.a - + @@ -399,7 +399,7 @@ explain select 1+row_number() over(partition by foo.c) from foo, bar where foo.a - + @@ -415,7 +415,7 @@ explain select 1+row_number() over(partition by foo.c) from foo, bar where foo.a - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-oid-oid.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-oid-oid.mdp index b74ce8ae5cfa..ab19aab072b4 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-oid-oid.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-oid-oid.mdp @@ -228,7 +228,7 @@ - + @@ -242,7 +242,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-txt-txt.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-txt-txt.mdp index 7a1f86558c23..63bb25d1fa04 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-txt-txt.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-txt-txt.mdp @@ -251,7 +251,7 @@ - + @@ -265,7 +265,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vc-txt.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vc-txt.mdp index cd91c9fae878..32ea8a9100f5 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vc-txt.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vc-txt.mdp @@ -306,7 +306,7 @@ - + @@ -320,7 +320,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vc-vc.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vc-vc.mdp index f790297dc19d..0c0ef5acf4b3 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vc-vc.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vc-vc.mdp @@ -273,7 +273,7 @@ - + @@ -287,7 +287,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vcpart-txt.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vcpart-txt.mdp index 5223ea1e1e36..c8ea7ec05890 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vcpart-txt.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesBCC-vcpart-txt.mdp @@ -750,7 +750,7 @@ - + @@ -767,7 +767,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesForLimit.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesForLimit.mdp index a528f5cc71c3..dab3172086a4 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesForLimit.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesForLimit.mdp @@ -276,7 +276,7 @@ a < 2 should also be outside the limit but not below. - + @@ -287,14 +287,14 @@ a < 2 should also be outside the limit but not below. - + - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesForPartSQ.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesForPartSQ.mdp index c45e042050b0..f61435fbe58a 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesForPartSQ.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesForPartSQ.mdp @@ -400,7 +400,7 @@ - + @@ -414,7 +414,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesFromExistsSubquery.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesFromExistsSubquery.mdp index 1945d4e5826f..e766ceba146a 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesFromExistsSubquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesFromExistsSubquery.mdp @@ -353,7 +353,7 @@ - + @@ -367,7 +367,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesFromMultiSubquery.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesFromMultiSubquery.mdp index edbd285db97f..0336085d0614 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesFromMultiSubquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesFromMultiSubquery.mdp @@ -520,32 +520,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -565,20 +542,10 @@ - - - - - - - - - - - + - + @@ -601,17 +568,17 @@ - - + + - - + + - + - + @@ -635,16 +602,16 @@ - + - - + + - + - + @@ -663,60 +630,79 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -727,15 +713,12 @@ - + - - - @@ -745,11 +728,14 @@ - + - + + + + @@ -757,48 +743,48 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + - + @@ -809,15 +795,12 @@ - + - - - @@ -827,11 +810,14 @@ - + - + + + + @@ -839,48 +825,48 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + - + @@ -890,36 +876,50 @@ - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesInnerOfLOJ.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesInnerOfLOJ.mdp index 1e23b81210e6..4ed811e6053a 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesInnerOfLOJ.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesInnerOfLOJ.mdp @@ -352,7 +352,7 @@ - + @@ -363,7 +363,7 @@ - + @@ -380,7 +380,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesIntFromExistsSubquery.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesIntFromExistsSubquery.mdp index 31945f698bc0..0f7bcb35abb5 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesIntFromExistsSubquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesIntFromExistsSubquery.mdp @@ -411,7 +411,7 @@ - + @@ -434,7 +434,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesIntFromSubquery.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesIntFromSubquery.mdp index c45be27f1009..8f6c2e9f84c6 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesIntFromSubquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesIntFromSubquery.mdp @@ -341,7 +341,7 @@ - + @@ -355,7 +355,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesJoinSubquery.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesJoinSubquery.mdp index a5dd436fab08..6390f9288a71 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesJoinSubquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesJoinSubquery.mdp @@ -486,7 +486,7 @@ - + @@ -524,7 +524,7 @@ - + @@ -572,7 +572,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferPredicatesMultiColumns.mdp b/src/backend/gporca/data/dxl/minidump/InferPredicatesMultiColumns.mdp index 959aad4e8047..cb10c4b0a92f 100644 --- a/src/backend/gporca/data/dxl/minidump/InferPredicatesMultiColumns.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferPredicatesMultiColumns.mdp @@ -426,7 +426,7 @@ - + @@ -449,7 +449,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InferredPredicatesConstraintSimplification.mdp b/src/backend/gporca/data/dxl/minidump/InferredPredicatesConstraintSimplification.mdp index 767d5c43aaef..aab7c10e0fa4 100644 --- a/src/backend/gporca/data/dxl/minidump/InferredPredicatesConstraintSimplification.mdp +++ b/src/backend/gporca/data/dxl/minidump/InferredPredicatesConstraintSimplification.mdp @@ -402,7 +402,7 @@ - + @@ -419,7 +419,7 @@ - + @@ -470,7 +470,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InnerJoinBroadcastTableHashSpec.mdp b/src/backend/gporca/data/dxl/minidump/InnerJoinBroadcastTableHashSpec.mdp index 57bbbd79a075..3aa0fa96bd75 100644 --- a/src/backend/gporca/data/dxl/minidump/InnerJoinBroadcastTableHashSpec.mdp +++ b/src/backend/gporca/data/dxl/minidump/InnerJoinBroadcastTableHashSpec.mdp @@ -363,7 +363,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + @@ -379,7 +379,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + @@ -397,7 +397,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + @@ -420,7 +420,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + @@ -438,7 +438,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + diff --git a/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExcept.mdp b/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExcept.mdp index ff2c009499f4..4333cbeab6b1 100644 --- a/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExcept.mdp +++ b/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExcept.mdp @@ -298,7 +298,7 @@ ON (a.col = b.col); - + @@ -320,7 +320,7 @@ ON (a.col = b.col); - + @@ -334,7 +334,7 @@ ON (a.col = b.col); - + @@ -374,7 +374,7 @@ ON (a.col = b.col); - + @@ -387,7 +387,7 @@ ON (a.col = b.col); - + @@ -402,7 +402,7 @@ ON (a.col = b.col); - + @@ -421,7 +421,7 @@ ON (a.col = b.col); - + diff --git a/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExceptAll.mdp b/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExceptAll.mdp index ac8725550f94..001f5f60c2b8 100644 --- a/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExceptAll.mdp +++ b/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExceptAll.mdp @@ -336,7 +336,7 @@ ON (a.col = b.col); - + @@ -358,7 +358,7 @@ ON (a.col = b.col); - + @@ -372,7 +372,7 @@ ON (a.col = b.col); - + @@ -412,7 +412,7 @@ ON (a.col = b.col); - + @@ -437,7 +437,7 @@ ON (a.col = b.col); - + @@ -450,7 +450,7 @@ ON (a.col = b.col); - + @@ -465,7 +465,7 @@ ON (a.col = b.col); - + diff --git a/src/backend/gporca/data/dxl/minidump/InnerJoinReplicatedTableHashSpec.mdp b/src/backend/gporca/data/dxl/minidump/InnerJoinReplicatedTableHashSpec.mdp index 6ac1e7bf4692..581acdf46307 100644 --- a/src/backend/gporca/data/dxl/minidump/InnerJoinReplicatedTableHashSpec.mdp +++ b/src/backend/gporca/data/dxl/minidump/InnerJoinReplicatedTableHashSpec.mdp @@ -361,7 +361,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + @@ -377,7 +377,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + @@ -395,7 +395,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + @@ -418,7 +418,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + @@ -436,7 +436,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + @@ -455,7 +455,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1, t2 WHERE a1 = b2 GROUP BY b2 ORDER BY b2; - + diff --git a/src/backend/gporca/data/dxl/minidump/Insert-With-HJ-CTE-Agg.mdp b/src/backend/gporca/data/dxl/minidump/Insert-With-HJ-CTE-Agg.mdp index b30a407031ed..50b7b3aab77d 100644 --- a/src/backend/gporca/data/dxl/minidump/Insert-With-HJ-CTE-Agg.mdp +++ b/src/backend/gporca/data/dxl/minidump/Insert-With-HJ-CTE-Agg.mdp @@ -511,7 +511,7 @@ - + @@ -549,7 +549,7 @@ - + @@ -581,7 +581,7 @@ - + @@ -609,7 +609,7 @@ - + @@ -632,7 +632,7 @@ - + @@ -655,7 +655,7 @@ - + @@ -680,7 +680,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertNonSingleton.mdp b/src/backend/gporca/data/dxl/minidump/InsertNonSingleton.mdp index c199c55019c6..481a4ca4cfbb 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertNonSingleton.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertNonSingleton.mdp @@ -304,7 +304,7 @@ EXPLAIN INSERT INTO snackbox SELECT a FROM (SELECT c FROM hottoast LIMIT 3) hott - + @@ -326,7 +326,7 @@ EXPLAIN INSERT INTO snackbox SELECT a FROM (SELECT c FROM hottoast LIMIT 3) hott - + @@ -340,7 +340,7 @@ EXPLAIN INSERT INTO snackbox SELECT a FROM (SELECT c FROM hottoast LIMIT 3) hott - + @@ -351,7 +351,7 @@ EXPLAIN INSERT INTO snackbox SELECT a FROM (SELECT c FROM hottoast LIMIT 3) hott - + diff --git a/src/backend/gporca/data/dxl/minidump/Intersect-OuterRefs.mdp b/src/backend/gporca/data/dxl/minidump/Intersect-OuterRefs.mdp index 8de33211bc95..0932785d1cdf 100644 --- a/src/backend/gporca/data/dxl/minidump/Intersect-OuterRefs.mdp +++ b/src/backend/gporca/data/dxl/minidump/Intersect-OuterRefs.mdp @@ -1416,7 +1416,7 @@ - + @@ -1436,7 +1436,7 @@ - + @@ -1523,7 +1523,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Intersect-Volatile-Func.mdp b/src/backend/gporca/data/dxl/minidump/Intersect-Volatile-Func.mdp index a59de422794e..bb90193c8550 100644 --- a/src/backend/gporca/data/dxl/minidump/Intersect-Volatile-Func.mdp +++ b/src/backend/gporca/data/dxl/minidump/Intersect-Volatile-Func.mdp @@ -112,7 +112,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JOIN-NonRedistributableCol.mdp b/src/backend/gporca/data/dxl/minidump/JOIN-NonRedistributableCol.mdp index ffa6ff1a5381..220af1e5fb61 100644 --- a/src/backend/gporca/data/dxl/minidump/JOIN-NonRedistributableCol.mdp +++ b/src/backend/gporca/data/dxl/minidump/JOIN-NonRedistributableCol.mdp @@ -273,7 +273,7 @@ explain select * from foo, testhstore where foo.b = testhstore.h; - + @@ -290,7 +290,7 @@ explain select * from foo, testhstore where foo.b = testhstore.h; - + diff --git a/src/backend/gporca/data/dxl/minidump/JOIN-Pred-Cast-Int4.mdp b/src/backend/gporca/data/dxl/minidump/JOIN-Pred-Cast-Int4.mdp index bb02b0cbd584..05c767e122f8 100644 --- a/src/backend/gporca/data/dxl/minidump/JOIN-Pred-Cast-Int4.mdp +++ b/src/backend/gporca/data/dxl/minidump/JOIN-Pred-Cast-Int4.mdp @@ -456,7 +456,7 @@ - + @@ -470,7 +470,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JOIN-Pred-Cast-Varchar.mdp b/src/backend/gporca/data/dxl/minidump/JOIN-Pred-Cast-Varchar.mdp index 4f81ecc0e8a6..3b5c10f88f8e 100644 --- a/src/backend/gporca/data/dxl/minidump/JOIN-Pred-Cast-Varchar.mdp +++ b/src/backend/gporca/data/dxl/minidump/JOIN-Pred-Cast-Varchar.mdp @@ -652,7 +652,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JOIN-cast2text-int4-Eq-cast2text-double.mdp b/src/backend/gporca/data/dxl/minidump/JOIN-cast2text-int4-Eq-cast2text-double.mdp index 0df2ae9e6b6e..292c09fdbc6a 100644 --- a/src/backend/gporca/data/dxl/minidump/JOIN-cast2text-int4-Eq-cast2text-double.mdp +++ b/src/backend/gporca/data/dxl/minidump/JOIN-cast2text-int4-Eq-cast2text-double.mdp @@ -477,7 +477,7 @@ - + @@ -491,7 +491,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JOIN-int4-Eq-double.mdp b/src/backend/gporca/data/dxl/minidump/JOIN-int4-Eq-double.mdp index 5e72b633a132..63dc2dde69b3 100644 --- a/src/backend/gporca/data/dxl/minidump/JOIN-int4-Eq-double.mdp +++ b/src/backend/gporca/data/dxl/minidump/JOIN-int4-Eq-double.mdp @@ -456,7 +456,7 @@ - + @@ -470,7 +470,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JOIN-int4-Eq-int2.mdp b/src/backend/gporca/data/dxl/minidump/JOIN-int4-Eq-int2.mdp index df18f20696c9..ea318e4177a3 100644 --- a/src/backend/gporca/data/dxl/minidump/JOIN-int4-Eq-int2.mdp +++ b/src/backend/gporca/data/dxl/minidump/JOIN-int4-Eq-int2.mdp @@ -355,7 +355,7 @@ - + @@ -369,7 +369,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Join-Disj-Subqs.mdp b/src/backend/gporca/data/dxl/minidump/Join-Disj-Subqs.mdp index 4c76be1dce60..2f2de0ed099d 100644 --- a/src/backend/gporca/data/dxl/minidump/Join-Disj-Subqs.mdp +++ b/src/backend/gporca/data/dxl/minidump/Join-Disj-Subqs.mdp @@ -988,7 +988,7 @@ OR - + diff --git a/src/backend/gporca/data/dxl/minidump/Join-INDF.mdp b/src/backend/gporca/data/dxl/minidump/Join-INDF.mdp index 2bc54c8025df..373a0be9c14f 100644 --- a/src/backend/gporca/data/dxl/minidump/Join-INDF.mdp +++ b/src/backend/gporca/data/dxl/minidump/Join-INDF.mdp @@ -347,7 +347,7 @@ select * from foo, foo2 where foo.a is not distinct from foo2.a; - + @@ -364,7 +364,7 @@ select * from foo, foo2 where foo.a is not distinct from foo2.a; - + diff --git a/src/backend/gporca/data/dxl/minidump/Join-Varchar-Equality.mdp b/src/backend/gporca/data/dxl/minidump/Join-Varchar-Equality.mdp index 8abb5beae924..e1dd56b70c4d 100644 --- a/src/backend/gporca/data/dxl/minidump/Join-Varchar-Equality.mdp +++ b/src/backend/gporca/data/dxl/minidump/Join-Varchar-Equality.mdp @@ -3840,7 +3840,7 @@ - + @@ -3860,7 +3860,7 @@ - + @@ -3880,7 +3880,7 @@ - + @@ -3915,7 +3915,7 @@ - + @@ -3952,7 +3952,7 @@ - + @@ -3975,7 +3975,7 @@ - + @@ -4010,7 +4010,7 @@ - + @@ -4047,7 +4047,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAboveLimit.mdp b/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAboveLimit.mdp index c9f81b465662..56f38af7f478 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAboveLimit.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAboveLimit.mdp @@ -339,7 +339,7 @@ - + @@ -353,7 +353,7 @@ - + @@ -404,7 +404,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAtLimit.mdp b/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAtLimit.mdp index 6e16970c61ca..18f635025bc7 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAtLimit.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAtLimit.mdp @@ -339,7 +339,7 @@ - + @@ -353,7 +353,7 @@ - + @@ -404,7 +404,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityBelowLimit.mdp b/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityBelowLimit.mdp index a2d7c774aa9a..b42351e85bed 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityBelowLimit.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityBelowLimit.mdp @@ -339,7 +339,7 @@ - + @@ -353,7 +353,7 @@ - + @@ -377,7 +377,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinCitextVarchar.mdp b/src/backend/gporca/data/dxl/minidump/JoinCitextVarchar.mdp index 66809635252b..4119513c9f1d 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinCitextVarchar.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinCitextVarchar.mdp @@ -350,7 +350,7 @@ - + @@ -370,7 +370,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinColWithOnlyNDV.mdp b/src/backend/gporca/data/dxl/minidump/JoinColWithOnlyNDV.mdp index 0a08bbcecbd9..abeede746366 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinColWithOnlyNDV.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinColWithOnlyNDV.mdp @@ -11702,7 +11702,7 @@ - + @@ -11890,7 +11890,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinCombinedHashSpecNullsNotColocated.mdp b/src/backend/gporca/data/dxl/minidump/JoinCombinedHashSpecNullsNotColocated.mdp index 85c8d5042c6f..9534d9f83bc7 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinCombinedHashSpecNullsNotColocated.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinCombinedHashSpecNullsNotColocated.mdp @@ -365,7 +365,7 @@ - + @@ -379,7 +379,7 @@ - + @@ -396,7 +396,7 @@ - + @@ -415,7 +415,7 @@ - + @@ -443,7 +443,7 @@ - + @@ -544,7 +544,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinDefaultOpfamiliesUsingNonDefaultOpfamilyOp.mdp b/src/backend/gporca/data/dxl/minidump/JoinDefaultOpfamiliesUsingNonDefaultOpfamilyOp.mdp index 5b18586455df..aafb97f440b7 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinDefaultOpfamiliesUsingNonDefaultOpfamilyOp.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinDefaultOpfamiliesUsingNonDefaultOpfamilyOp.mdp @@ -336,7 +336,7 @@ - + @@ -350,7 +350,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinNDVRemain.mdp b/src/backend/gporca/data/dxl/minidump/JoinNDVRemain.mdp index d77343ee7ee6..83809aca8118 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinNDVRemain.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinNDVRemain.mdp @@ -3158,7 +3158,7 @@ select * from customer_address, store where customer_address.ca_county::text = s - + @@ -3292,7 +3292,7 @@ select * from customer_address, store where customer_address.ca_county::text = s - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinOnViewWithCastedColumn.mdp b/src/backend/gporca/data/dxl/minidump/JoinOnViewWithCastedColumn.mdp index 9642c794de11..aa319d29aded 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinOnViewWithCastedColumn.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinOnViewWithCastedColumn.mdp @@ -348,7 +348,7 @@ - + @@ -368,7 +368,7 @@ - + @@ -394,7 +394,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinOnViewWithCastedColumnAndSubqueryInPredicate.mdp b/src/backend/gporca/data/dxl/minidump/JoinOnViewWithCastedColumnAndSubqueryInPredicate.mdp index 6746a8f1f0f9..1b6bbcf4fb64 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinOnViewWithCastedColumnAndSubqueryInPredicate.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinOnViewWithCastedColumnAndSubqueryInPredicate.mdp @@ -393,7 +393,7 @@ - + @@ -413,7 +413,7 @@ - + @@ -439,7 +439,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinOnViewWithMixOfPushableAndNonpushablePredicates.mdp b/src/backend/gporca/data/dxl/minidump/JoinOnViewWithMixOfPushableAndNonpushablePredicates.mdp index f878d9de07ef..bdc457969a84 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinOnViewWithMixOfPushableAndNonpushablePredicates.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinOnViewWithMixOfPushableAndNonpushablePredicates.mdp @@ -381,7 +381,7 @@ - + @@ -401,7 +401,7 @@ - + @@ -426,7 +426,7 @@ - + @@ -451,7 +451,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinOnViewWithSetReturningColumn.mdp b/src/backend/gporca/data/dxl/minidump/JoinOnViewWithSetReturningColumn.mdp index 8e236754848c..783b12914fca 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinOnViewWithSetReturningColumn.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinOnViewWithSetReturningColumn.mdp @@ -318,7 +318,7 @@ - + @@ -338,7 +338,7 @@ - + @@ -363,7 +363,7 @@ - + @@ -386,7 +386,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinOnViewWithVolatileColumn.mdp b/src/backend/gporca/data/dxl/minidump/JoinOnViewWithVolatileColumn.mdp index e7bf9e03b502..be01160107b8 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinOnViewWithVolatileColumn.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinOnViewWithVolatileColumn.mdp @@ -372,7 +372,7 @@ - + @@ -392,7 +392,7 @@ - + @@ -417,7 +417,7 @@ - + @@ -442,7 +442,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelGreedyNonPartTblInnerJoin.mdp b/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelGreedyNonPartTblInnerJoin.mdp index 3aa1bdf56048..836132437297 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelGreedyNonPartTblInnerJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelGreedyNonPartTblInnerJoin.mdp @@ -2871,7 +2871,7 @@ - + @@ -2903,7 +2903,7 @@ - + @@ -2941,7 +2941,7 @@ - + @@ -2973,7 +2973,7 @@ - + @@ -2998,7 +2998,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelQuery3WayHashJoinPartTbl.mdp b/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelQuery3WayHashJoinPartTbl.mdp index cdc51ac13a67..5f3c433e2250 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelQuery3WayHashJoinPartTbl.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelQuery3WayHashJoinPartTbl.mdp @@ -1146,7 +1146,7 @@ - + @@ -1172,7 +1172,7 @@ - + @@ -1204,7 +1204,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelQueryNonPartTblInnerJoin.mdp b/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelQueryNonPartTblInnerJoin.mdp index bd4f69298c5b..29c61db75650 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelQueryNonPartTblInnerJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinOptimizationLevelQueryNonPartTblInnerJoin.mdp @@ -2871,7 +2871,7 @@ - + @@ -2903,7 +2903,7 @@ - + @@ -2941,7 +2941,7 @@ - + @@ -2973,7 +2973,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinOrderDPE.mdp b/src/backend/gporca/data/dxl/minidump/JoinOrderDPE.mdp index 880f60c1e3bb..bc7acebf9603 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinOrderDPE.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinOrderDPE.mdp @@ -1685,7 +1685,7 @@ - + @@ -1717,7 +1717,7 @@ - + @@ -1737,7 +1737,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinTinterval.mdp b/src/backend/gporca/data/dxl/minidump/JoinTinterval.mdp index 063ea5b5bc10..0eee3b87b0e7 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinTinterval.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinTinterval.mdp @@ -314,7 +314,7 @@ - + @@ -331,7 +331,7 @@ - + @@ -348,7 +348,7 @@ - + @@ -367,7 +367,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LASJ-Not-In-Force-Broadcast.mdp b/src/backend/gporca/data/dxl/minidump/LASJ-Not-In-Force-Broadcast.mdp index 9575d801d76c..30545ab7daa7 100644 --- a/src/backend/gporca/data/dxl/minidump/LASJ-Not-In-Force-Broadcast.mdp +++ b/src/backend/gporca/data/dxl/minidump/LASJ-Not-In-Force-Broadcast.mdp @@ -1142,7 +1142,7 @@ - + @@ -1153,7 +1153,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ-HashJoin-MultiDistKeys-WithComplexPreds.mdp b/src/backend/gporca/data/dxl/minidump/LOJ-HashJoin-MultiDistKeys-WithComplexPreds.mdp index 8a99df389484..21e7f83cbcfc 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ-HashJoin-MultiDistKeys-WithComplexPreds.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ-HashJoin-MultiDistKeys-WithComplexPreds.mdp @@ -290,7 +290,7 @@ - + @@ -310,7 +310,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ-IsNullPred.mdp b/src/backend/gporca/data/dxl/minidump/LOJ-IsNullPred.mdp index 1c671ec6f9aa..1a9a6dd34eb3 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ-IsNullPred.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ-IsNullPred.mdp @@ -12162,7 +12162,7 @@ - + @@ -12299,7 +12299,7 @@ - + @@ -12440,7 +12440,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ-PushDown.mdp b/src/backend/gporca/data/dxl/minidump/LOJ-PushDown.mdp index 9c77ae8d5e98..4b8c1849a641 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ-PushDown.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ-PushDown.mdp @@ -382,7 +382,7 @@ explain select 1 from push_t3 where exists ( - + @@ -393,7 +393,7 @@ explain select 1 from push_t3 where exists ( - + @@ -408,7 +408,7 @@ explain select 1 from push_t3 where exists ( - + @@ -424,7 +424,7 @@ explain select 1 from push_t3 where exists ( - + @@ -434,7 +434,7 @@ explain select 1 from push_t3 where exists ( - + @@ -445,7 +445,7 @@ explain select 1 from push_t3 where exists ( - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ-With-Agg.mdp b/src/backend/gporca/data/dxl/minidump/LOJ-With-Agg.mdp index ee9548a2608b..c08259ef2892 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ-With-Agg.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ-With-Agg.mdp @@ -298,7 +298,7 @@ - + @@ -312,7 +312,7 @@ - + @@ -335,7 +335,7 @@ - + @@ -353,7 +353,7 @@ - + @@ -372,7 +372,7 @@ - + @@ -386,7 +386,7 @@ - + @@ -407,7 +407,7 @@ - + @@ -422,7 +422,7 @@ - + @@ -433,7 +433,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJNonNullRejectingPredicates.mdp b/src/backend/gporca/data/dxl/minidump/LOJNonNullRejectingPredicates.mdp index 8f5a31a942e7..9af5b9e38b43 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJNonNullRejectingPredicates.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJNonNullRejectingPredicates.mdp @@ -434,7 +434,7 @@ - + @@ -490,7 +490,7 @@ - + @@ -550,7 +550,7 @@ - + @@ -612,7 +612,7 @@ - + @@ -662,7 +662,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJNullRejectingPredicates.mdp b/src/backend/gporca/data/dxl/minidump/LOJNullRejectingPredicates.mdp index 4641e26a63c0..228395db2039 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJNullRejectingPredicates.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJNullRejectingPredicates.mdp @@ -432,7 +432,7 @@ explain select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a lef - + @@ -488,7 +488,7 @@ explain select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a lef - + @@ -550,7 +550,7 @@ explain select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a lef - + @@ -664,7 +664,7 @@ explain select * from t1 left join t2 on t1.a=t2.a left join t3 on t2.a=t3.a lef - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJNullRejectingZeroPlacePredicates.mdp b/src/backend/gporca/data/dxl/minidump/LOJNullRejectingZeroPlacePredicates.mdp index e3bfcd7b8ecb..882cccea5574 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJNullRejectingZeroPlacePredicates.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJNullRejectingZeroPlacePredicates.mdp @@ -415,158 +415,158 @@ - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + - - - + - + - - - - - - - - - - - - - + - + + - + - - - - - - - + - + + + + - + - + + + + + - - + + + + + + + + + + + + + - + - + + + + + + + + + + + - - - - - - - - + - + - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/LOJReorderComplexNestedLOJs.mdp b/src/backend/gporca/data/dxl/minidump/LOJReorderComplexNestedLOJs.mdp index 49a29fcc4b04..6033b4ab674f 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJReorderComplexNestedLOJs.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJReorderComplexNestedLOJs.mdp @@ -1622,7 +1622,7 @@ - + @@ -1678,7 +1678,7 @@ - + @@ -1707,7 +1707,7 @@ - + @@ -1769,7 +1769,7 @@ - + @@ -1877,7 +1877,7 @@ - + @@ -1906,7 +1906,7 @@ - + @@ -1945,7 +1945,7 @@ - + @@ -2007,7 +2007,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJReorderWithComplexPredicate.mdp b/src/backend/gporca/data/dxl/minidump/LOJReorderWithComplexPredicate.mdp index e088da97620b..f2d51707fddb 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJReorderWithComplexPredicate.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJReorderWithComplexPredicate.mdp @@ -1960,7 +1960,7 @@ - + @@ -1995,7 +1995,7 @@ - + @@ -2040,7 +2040,7 @@ - + @@ -2112,7 +2112,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJReorderWithIDF.mdp b/src/backend/gporca/data/dxl/minidump/LOJReorderWithIDF.mdp index 233ceac3ba98..1f6f334d4614 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJReorderWithIDF.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJReorderWithIDF.mdp @@ -1543,7 +1543,7 @@ - + @@ -1578,7 +1578,7 @@ - + @@ -1618,7 +1618,7 @@ - + @@ -1690,7 +1690,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJReorderWithNestedLOJAndFilter.mdp b/src/backend/gporca/data/dxl/minidump/LOJReorderWithNestedLOJAndFilter.mdp index 70aa3e633bfc..e887bbfba3f6 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJReorderWithNestedLOJAndFilter.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJReorderWithNestedLOJAndFilter.mdp @@ -1543,7 +1543,7 @@ - + @@ -1578,7 +1578,7 @@ - + @@ -1623,7 +1623,7 @@ - + @@ -1695,7 +1695,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJReorderWithOneSidedFilter.mdp b/src/backend/gporca/data/dxl/minidump/LOJReorderWithOneSidedFilter.mdp index cb728e0b1e2d..6f1e7cfae370 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJReorderWithOneSidedFilter.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJReorderWithOneSidedFilter.mdp @@ -1957,7 +1957,7 @@ - + @@ -1992,7 +1992,7 @@ - + @@ -2037,7 +2037,7 @@ - + @@ -2074,7 +2074,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJReorderWithSimplePredicate.mdp b/src/backend/gporca/data/dxl/minidump/LOJReorderWithSimplePredicate.mdp index 4165d5e01acb..eac2d7dc0046 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJReorderWithSimplePredicate.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJReorderWithSimplePredicate.mdp @@ -1540,10 +1540,10 @@ - + - + @@ -1578,7 +1578,7 @@ - + @@ -1654,7 +1654,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ_IDF_no_convert_outer_ref_predicate_with_NULL.mdp b/src/backend/gporca/data/dxl/minidump/LOJ_IDF_no_convert_outer_ref_predicate_with_NULL.mdp index 9179f4f41a33..f34891644fc2 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ_IDF_no_convert_outer_ref_predicate_with_NULL.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ_IDF_no_convert_outer_ref_predicate_with_NULL.mdp @@ -283,7 +283,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t1.b IS DISTINC - + @@ -303,7 +303,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t1.b IS DISTINC - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ_IDF_no_convert_outer_ref_predicate_with_inner_ref.mdp b/src/backend/gporca/data/dxl/minidump/LOJ_IDF_no_convert_outer_ref_predicate_with_inner_ref.mdp index e4816e858858..bec90eb2f614 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ_IDF_no_convert_outer_ref_predicate_with_inner_ref.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ_IDF_no_convert_outer_ref_predicate_with_inner_ref.mdp @@ -285,7 +285,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t1.b IS DISTINC - + @@ -305,7 +305,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t1.b IS DISTINC - + @@ -330,7 +330,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t1.b IS DISTINC - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ_bb_mpph.mdp b/src/backend/gporca/data/dxl/minidump/LOJ_bb_mpph.mdp index fd3de7990fe8..1b675094d8e9 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ_bb_mpph.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ_bb_mpph.mdp @@ -5776,7 +5776,7 @@ - + @@ -5791,7 +5791,7 @@ - + @@ -5811,7 +5811,7 @@ - + @@ -5828,7 +5828,7 @@ - + @@ -5847,7 +5847,7 @@ - + @@ -5870,7 +5870,7 @@ - + @@ -5887,7 +5887,7 @@ - + @@ -5906,7 +5906,7 @@ - + @@ -5925,7 +5925,7 @@ - + @@ -5942,7 +5942,7 @@ - + @@ -5961,7 +5961,7 @@ - + @@ -5982,7 +5982,7 @@ - + @@ -6005,7 +6005,7 @@ - + @@ -6098,7 +6098,7 @@ - + @@ -6115,7 +6115,7 @@ - + @@ -6167,7 +6167,7 @@ - + @@ -6228,7 +6228,7 @@ - + @@ -6249,7 +6249,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ_convert_to_inner_with_inner_predicate_IDF.mdp b/src/backend/gporca/data/dxl/minidump/LOJ_convert_to_inner_with_inner_predicate_IDF.mdp index 96d3c72ff9fe..871c8276286b 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ_convert_to_inner_with_inner_predicate_IDF.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ_convert_to_inner_with_inner_predicate_IDF.mdp @@ -276,7 +276,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS DISTINC - + @@ -296,7 +296,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS DISTINC - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_inner_predicate_IDF_const.mdp b/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_inner_predicate_IDF_const.mdp index bc2e327012ec..b49c5accd86e 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_inner_predicate_IDF_const.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_inner_predicate_IDF_const.mdp @@ -277,7 +277,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS DISTINC - + @@ -297,7 +297,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS DISTINC - + @@ -322,7 +322,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS DISTINC - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_outer_predicate_INDF.mdp b/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_outer_predicate_INDF.mdp index 3fbe99331fbc..b222e031d0ae 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_outer_predicate_INDF.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_outer_predicate_INDF.mdp @@ -279,7 +279,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS NOT DIS - + @@ -299,7 +299,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS NOT DIS - + @@ -326,7 +326,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS NOT DIS - + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_outer_predicate_INDF_const.mdp b/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_outer_predicate_INDF_const.mdp index f7ddcb30933d..b5fa09852b71 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_outer_predicate_INDF_const.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ_dont_convert_to_inner_with_outer_predicate_INDF_const.mdp @@ -279,7 +279,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS NOT DIS - + @@ -299,7 +299,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS NOT DIS - + @@ -326,7 +326,7 @@ EXPLAIN SELECT * FROM t1 LEFT OUTER JOIN t2 ON t1.a = t2.a WHERE t2.b IS NOT DIS - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftJoin-DPv2-With-Select.mdp b/src/backend/gporca/data/dxl/minidump/LeftJoin-DPv2-With-Select.mdp index 1502ce209483..6a7038128997 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftJoin-DPv2-With-Select.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftJoin-DPv2-With-Select.mdp @@ -351,7 +351,7 @@ - + @@ -377,7 +377,7 @@ - + @@ -405,7 +405,7 @@ - + @@ -433,7 +433,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftJoin-UnsupportedFilter-Cardinality.mdp b/src/backend/gporca/data/dxl/minidump/LeftJoin-UnsupportedFilter-Cardinality.mdp index 2bb7cde6d4af..0bf69393e50b 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftJoin-UnsupportedFilter-Cardinality.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftJoin-UnsupportedFilter-Cardinality.mdp @@ -790,7 +790,7 @@ explain select * from foo left join bar on foo.a=bar.a left join zoo on foo.a::i - + @@ -816,7 +816,7 @@ explain select * from foo left join bar on foo.a=bar.a left join zoo on foo.a::i - + @@ -852,7 +852,7 @@ explain select * from foo left join bar on foo.a=bar.a left join zoo on foo.a::i - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Coalesce.mdp b/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Coalesce.mdp index b231b765bcff..94cbf8977553 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Coalesce.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Coalesce.mdp @@ -704,7 +704,7 @@ where coalesce(t2.fname, t3.fname) is not null; - + @@ -715,7 +715,7 @@ where coalesce(t2.fname, t3.fname) is not null; - + @@ -728,7 +728,7 @@ where coalesce(t2.fname, t3.fname) is not null; - + @@ -743,7 +743,7 @@ where coalesce(t2.fname, t3.fname) is not null; - + @@ -759,7 +759,7 @@ where coalesce(t2.fname, t3.fname) is not null; - + @@ -772,7 +772,7 @@ where coalesce(t2.fname, t3.fname) is not null; - + @@ -786,7 +786,7 @@ where coalesce(t2.fname, t3.fname) is not null; - + @@ -809,7 +809,7 @@ where coalesce(t2.fname, t3.fname) is not null; - + @@ -829,7 +829,7 @@ where coalesce(t2.fname, t3.fname) is not null; - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Col-Const-Pred.mdp b/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Col-Const-Pred.mdp index a1b8fd6c45d1..af18f1846dad 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Col-Const-Pred.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Col-Const-Pred.mdp @@ -2113,7 +2113,7 @@ - + @@ -2276,7 +2276,7 @@ - + @@ -2342,7 +2342,7 @@ - + @@ -2475,7 +2475,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Pred-On-Inner.mdp b/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Pred-On-Inner.mdp index 6573be81d041..a0c36d0bd9e4 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Pred-On-Inner.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Pred-On-Inner.mdp @@ -257,7 +257,7 @@ - + @@ -277,7 +277,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Pred-On-Inner2.mdp b/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Pred-On-Inner2.mdp index 5c6543898e8b..4b0c693f77c8 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Pred-On-Inner2.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftJoin-With-Pred-On-Inner2.mdp @@ -397,7 +397,7 @@ - + @@ -411,7 +411,7 @@ - + @@ -435,7 +435,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftJoinBroadcastTableHashSpec.mdp b/src/backend/gporca/data/dxl/minidump/LeftJoinBroadcastTableHashSpec.mdp index 79f1ef6df562..383a13ae84a4 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftJoinBroadcastTableHashSpec.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftJoinBroadcastTableHashSpec.mdp @@ -369,7 +369,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1 LEFT JOIN t2 ON a1 = b2 GROUP BY b2 ORDER BY - + @@ -385,7 +385,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1 LEFT JOIN t2 ON a1 = b2 GROUP BY b2 ORDER BY - + @@ -403,7 +403,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1 LEFT JOIN t2 ON a1 = b2 GROUP BY b2 ORDER BY - + @@ -426,7 +426,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1 LEFT JOIN t2 ON a1 = b2 GROUP BY b2 ORDER BY - + @@ -444,7 +444,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1 LEFT JOIN t2 ON a1 = b2 GROUP BY b2 ORDER BY - + @@ -463,7 +463,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1 LEFT JOIN t2 ON a1 = b2 GROUP BY b2 ORDER BY - + @@ -477,7 +477,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1 LEFT JOIN t2 ON a1 = b2 GROUP BY b2 ORDER BY - + @@ -500,7 +500,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1 LEFT JOIN t2 ON a1 = b2 GROUP BY b2 ORDER BY - + @@ -518,7 +518,7 @@ EXPLAIN SELECT b2, sum(c1) FROM t1 LEFT JOIN t2 ON a1 = b2 GROUP BY b2 ORDER BY - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftJoinDPv2JoinOrder.mdp b/src/backend/gporca/data/dxl/minidump/LeftJoinDPv2JoinOrder.mdp index e5bce5f42cb8..72371a3fffc1 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftJoinDPv2JoinOrder.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftJoinDPv2JoinOrder.mdp @@ -1445,7 +1445,7 @@ - + @@ -1483,7 +1483,7 @@ - + @@ -1509,7 +1509,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftJoinNullsNotColocated.mdp b/src/backend/gporca/data/dxl/minidump/LeftJoinNullsNotColocated.mdp index 40ec6519daf9..199aedd223bd 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftJoinNullsNotColocated.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftJoinNullsNotColocated.mdp @@ -421,7 +421,7 @@ on id2 = id3 distributed by (id1); - + @@ -445,7 +445,7 @@ on id2 = id3 distributed by (id1); - + @@ -465,7 +465,7 @@ on id2 = id3 distributed by (id1); - + @@ -488,7 +488,7 @@ on id2 = id3 distributed by (id1); - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin-Tpcds.mdp b/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin-Tpcds.mdp index 393407dcb73f..b5adfcbe07a8 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin-Tpcds.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin-Tpcds.mdp @@ -11634,7 +11634,7 @@ select * from v2 left outer join v1 on v2.i_item_sk = v1.ss_item_sk limit 5; - + @@ -11646,7 +11646,7 @@ select * from v2 left outer join v1 on v2.i_item_sk = v1.ss_item_sk limit 5; - + @@ -11660,7 +11660,7 @@ select * from v2 left outer join v1 on v2.i_item_sk = v1.ss_item_sk limit 5; - + @@ -11672,7 +11672,7 @@ select * from v2 left outer join v1 on v2.i_item_sk = v1.ss_item_sk limit 5; - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin.mdp b/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin.mdp index 3c309e470c7f..9f76ab7af80e 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin.mdp @@ -304,7 +304,7 @@ and ei.entity_id = i.ad_id; - + @@ -321,7 +321,7 @@ and ei.entity_id = i.ad_id; - + @@ -425,7 +425,7 @@ and ei.entity_id = i.ad_id; - + @@ -440,7 +440,7 @@ and ei.entity_id = i.ad_id; - + @@ -503,7 +503,7 @@ and ei.entity_id = i.ad_id; - + @@ -665,7 +665,7 @@ and ei.entity_id = i.ad_id; - + @@ -763,7 +763,7 @@ and ei.entity_id = i.ad_id; - + @@ -780,7 +780,7 @@ and ei.entity_id = i.ad_id; - + diff --git a/src/backend/gporca/data/dxl/minidump/MDQAs-Grouping-OrderBy.mdp b/src/backend/gporca/data/dxl/minidump/MDQAs-Grouping-OrderBy.mdp index a6f968911ad2..c8455302b782 100644 --- a/src/backend/gporca/data/dxl/minidump/MDQAs-Grouping-OrderBy.mdp +++ b/src/backend/gporca/data/dxl/minidump/MDQAs-Grouping-OrderBy.mdp @@ -467,7 +467,7 @@ SQL: - + @@ -482,7 +482,7 @@ SQL: - + @@ -501,7 +501,7 @@ SQL: - + @@ -516,7 +516,7 @@ SQL: - + @@ -537,7 +537,7 @@ SQL: - + @@ -634,7 +634,7 @@ SQL: - + diff --git a/src/backend/gporca/data/dxl/minidump/MDQAs-Grouping.mdp b/src/backend/gporca/data/dxl/minidump/MDQAs-Grouping.mdp index 0b835973964e..66cc35b325ce 100644 --- a/src/backend/gporca/data/dxl/minidump/MDQAs-Grouping.mdp +++ b/src/backend/gporca/data/dxl/minidump/MDQAs-Grouping.mdp @@ -458,7 +458,7 @@ SQL: - + @@ -475,7 +475,7 @@ SQL: - + @@ -572,7 +572,7 @@ SQL: - + diff --git a/src/backend/gporca/data/dxl/minidump/MDQAs-Union.mdp b/src/backend/gporca/data/dxl/minidump/MDQAs-Union.mdp index 05061ae272fc..217517a60c5f 100644 --- a/src/backend/gporca/data/dxl/minidump/MDQAs-Union.mdp +++ b/src/backend/gporca/data/dxl/minidump/MDQAs-Union.mdp @@ -511,7 +511,7 @@ select a,count(distinct a), count(distinct b) from t1 group by a - + @@ -528,7 +528,7 @@ select a,count(distinct a), count(distinct b) from t1 group by a - + @@ -549,7 +549,7 @@ select a,count(distinct a), count(distinct b) from t1 group by a - + @@ -565,7 +565,7 @@ select a,count(distinct a), count(distinct b) from t1 group by a - + @@ -662,7 +662,7 @@ select a,count(distinct a), count(distinct b) from t1 group by a - + @@ -918,7 +918,7 @@ select a,count(distinct a), count(distinct b) from t1 group by a - + @@ -1015,7 +1015,7 @@ select a,count(distinct a), count(distinct b) from t1 group by a - + diff --git a/src/backend/gporca/data/dxl/minidump/ManyTextUnionsInSubquery.mdp b/src/backend/gporca/data/dxl/minidump/ManyTextUnionsInSubquery.mdp index afffc29aff83..7236684a62f1 100644 --- a/src/backend/gporca/data/dxl/minidump/ManyTextUnionsInSubquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/ManyTextUnionsInSubquery.mdp @@ -591,7 +591,7 @@ - + @@ -602,14 +602,14 @@ - + - + @@ -955,7 +955,7 @@ - + @@ -971,7 +971,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/MissingColStatsWithLOJ.mdp b/src/backend/gporca/data/dxl/minidump/MissingColStatsWithLOJ.mdp index 4267f663ed87..149c44ef1f60 100644 --- a/src/backend/gporca/data/dxl/minidump/MissingColStatsWithLOJ.mdp +++ b/src/backend/gporca/data/dxl/minidump/MissingColStatsWithLOJ.mdp @@ -200,7 +200,7 @@ - + @@ -214,7 +214,7 @@ - + @@ -232,7 +232,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/MotionHazard-MaterializeUnderResult.mdp b/src/backend/gporca/data/dxl/minidump/MotionHazard-MaterializeUnderResult.mdp index 23e8ecccf57f..8fb4e781cad9 100644 --- a/src/backend/gporca/data/dxl/minidump/MotionHazard-MaterializeUnderResult.mdp +++ b/src/backend/gporca/data/dxl/minidump/MotionHazard-MaterializeUnderResult.mdp @@ -415,7 +415,7 @@ Optimizer status: PQO version 2.67.0 - + @@ -523,7 +523,7 @@ Optimizer status: PQO version 2.67.0 - + diff --git a/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeGatherUnderResult.mdp b/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeGatherUnderResult.mdp index 758aeaac9a76..4db5c9847307 100644 --- a/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeGatherUnderResult.mdp +++ b/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeGatherUnderResult.mdp @@ -411,7 +411,7 @@ explain select tab3.k, (select tab2.k from tab2 where tab2.i = tab1.i limit 1) a - + @@ -519,7 +519,7 @@ explain select tab3.k, (select tab2.k from tab2 where tab2.i = tab1.i limit 1) a - + diff --git a/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeHashAggUnderResult.mdp b/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeHashAggUnderResult.mdp index 94511c3ff438..82883e3c74d6 100644 --- a/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeHashAggUnderResult.mdp +++ b/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeHashAggUnderResult.mdp @@ -450,7 +450,7 @@ explain select f.k, f.subq from (select tab3.k, (select tab2.k from tab2 where t - + @@ -464,7 +464,7 @@ explain select f.k, f.subq from (select tab3.k, (select tab2.k from tab2 where t - + @@ -480,7 +480,7 @@ explain select f.k, f.subq from (select tab3.k, (select tab2.k from tab2 where t - + @@ -494,7 +494,7 @@ explain select f.k, f.subq from (select tab3.k, (select tab2.k from tab2 where t - + @@ -508,7 +508,7 @@ explain select f.k, f.subq from (select tab3.k, (select tab2.k from tab2 where t - + @@ -616,7 +616,7 @@ explain select f.k, f.subq from (select tab3.k, (select tab2.k from tab2 where t - + @@ -633,7 +633,7 @@ explain select f.k, f.subq from (select tab3.k, (select tab2.k from tab2 where t - + @@ -655,7 +655,7 @@ explain select f.k, f.subq from (select tab3.k, (select tab2.k from tab2 where t - + @@ -672,7 +672,7 @@ explain select f.k, f.subq from (select tab3.k, (select tab2.k from tab2 where t - + @@ -686,7 +686,7 @@ explain select f.k, f.subq from (select tab3.k, (select tab2.k from tab2 where t - + diff --git a/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeSortUnderResult.mdp b/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeSortUnderResult.mdp index feb344eb43bf..a9a4a6123ffa 100644 --- a/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeSortUnderResult.mdp +++ b/src/backend/gporca/data/dxl/minidump/MotionHazard-NoMaterializeSortUnderResult.mdp @@ -436,7 +436,7 @@ explain select tab3.k, (select tab2.k from tab2 where tab2.i = tab1.i limit 1) a - + @@ -544,7 +544,7 @@ explain select tab3.k, (select tab2.k from tab2 where tab2.i = tab1.i limit 1) a - + @@ -562,7 +562,7 @@ explain select tab3.k, (select tab2.k from tab2 where tab2.i = tab1.i limit 1) a - + diff --git a/src/backend/gporca/data/dxl/minidump/MultiColumnDQA-InnerJoin-GroupBy-HashAggregate.mdp b/src/backend/gporca/data/dxl/minidump/MultiColumnDQA-InnerJoin-GroupBy-HashAggregate.mdp index 6d4c26560c98..6970ff0f3316 100644 --- a/src/backend/gporca/data/dxl/minidump/MultiColumnDQA-InnerJoin-GroupBy-HashAggregate.mdp +++ b/src/backend/gporca/data/dxl/minidump/MultiColumnDQA-InnerJoin-GroupBy-HashAggregate.mdp @@ -450,7 +450,7 @@ - + @@ -461,7 +461,7 @@ - + @@ -472,7 +472,7 @@ - + @@ -509,7 +509,7 @@ - + @@ -538,7 +538,7 @@ - + @@ -564,7 +564,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/MultiDistKeyJoinCardinality.mdp b/src/backend/gporca/data/dxl/minidump/MultiDistKeyJoinCardinality.mdp index 3f8b11b92ab5..75daf035296b 100644 --- a/src/backend/gporca/data/dxl/minidump/MultiDistKeyJoinCardinality.mdp +++ b/src/backend/gporca/data/dxl/minidump/MultiDistKeyJoinCardinality.mdp @@ -1182,7 +1182,7 @@ - + @@ -1202,7 +1202,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/MultiDistKeyWithOtherPredsJoinCardinality.mdp b/src/backend/gporca/data/dxl/minidump/MultiDistKeyWithOtherPredsJoinCardinality.mdp index 52e9275c99b6..66cc0b39c190 100644 --- a/src/backend/gporca/data/dxl/minidump/MultiDistKeyWithOtherPredsJoinCardinality.mdp +++ b/src/backend/gporca/data/dxl/minidump/MultiDistKeyWithOtherPredsJoinCardinality.mdp @@ -687,10 +687,10 @@ - + - + @@ -722,7 +722,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/MultiLevel-IN-Subquery.mdp b/src/backend/gporca/data/dxl/minidump/MultiLevel-IN-Subquery.mdp index 3e6e9edc18c6..78393bf3dda8 100644 --- a/src/backend/gporca/data/dxl/minidump/MultiLevel-IN-Subquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/MultiLevel-IN-Subquery.mdp @@ -689,7 +689,7 @@ - + @@ -703,7 +703,7 @@ - + @@ -750,7 +750,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/MultiLevelDecorrelationWithSemiJoins.mdp b/src/backend/gporca/data/dxl/minidump/MultiLevelDecorrelationWithSemiJoins.mdp index 0a3adb5be46e..71fda49641cf 100644 --- a/src/backend/gporca/data/dxl/minidump/MultiLevelDecorrelationWithSemiJoins.mdp +++ b/src/backend/gporca/data/dxl/minidump/MultiLevelDecorrelationWithSemiJoins.mdp @@ -410,7 +410,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/MultipleDampedPredJoinCardinality.mdp b/src/backend/gporca/data/dxl/minidump/MultipleDampedPredJoinCardinality.mdp index 0ed1f2dd1673..366aeefcea67 100644 --- a/src/backend/gporca/data/dxl/minidump/MultipleDampedPredJoinCardinality.mdp +++ b/src/backend/gporca/data/dxl/minidump/MultipleDampedPredJoinCardinality.mdp @@ -1975,7 +1975,7 @@ - + @@ -1995,7 +1995,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/MultipleIndependentPredJoinCardinality.mdp b/src/backend/gporca/data/dxl/minidump/MultipleIndependentPredJoinCardinality.mdp index 28119b57e566..53a370706fa2 100644 --- a/src/backend/gporca/data/dxl/minidump/MultipleIndependentPredJoinCardinality.mdp +++ b/src/backend/gporca/data/dxl/minidump/MultipleIndependentPredJoinCardinality.mdp @@ -5757,7 +5757,7 @@ explain select * from t1, t2, t3 where t1.a = t2.a and t2.a = t3.a and t1.b = t3 - + @@ -5783,7 +5783,7 @@ explain select * from t1, t2, t3 where t1.a = t2.a and t2.a = t3.a and t1.b = t3 - + @@ -5819,7 +5819,7 @@ explain select * from t1, t2, t3 where t1.a = t2.a and t2.a = t3.a and t1.b = t3 - + diff --git a/src/backend/gporca/data/dxl/minidump/MultipleUpdateWithJoinOnDistCol.mdp b/src/backend/gporca/data/dxl/minidump/MultipleUpdateWithJoinOnDistCol.mdp index 39cb5652be1e..0f11dcd8f792 100644 --- a/src/backend/gporca/data/dxl/minidump/MultipleUpdateWithJoinOnDistCol.mdp +++ b/src/backend/gporca/data/dxl/minidump/MultipleUpdateWithJoinOnDistCol.mdp @@ -304,7 +304,7 @@ - + @@ -330,7 +330,7 @@ - + @@ -358,7 +358,7 @@ - + @@ -379,7 +379,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/NOT-IN-ArrayCmp.mdp b/src/backend/gporca/data/dxl/minidump/NOT-IN-ArrayCmp.mdp index 1bd9ed06a630..a9a50fbeb578 100644 --- a/src/backend/gporca/data/dxl/minidump/NOT-IN-ArrayCmp.mdp +++ b/src/backend/gporca/data/dxl/minidump/NOT-IN-ArrayCmp.mdp @@ -278,7 +278,7 @@ - + @@ -298,7 +298,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/NOT-IN-NotNullBoth.mdp b/src/backend/gporca/data/dxl/minidump/NOT-IN-NotNullBoth.mdp index ffa732b7cc31..bff652f88e12 100644 --- a/src/backend/gporca/data/dxl/minidump/NOT-IN-NotNullBoth.mdp +++ b/src/backend/gporca/data/dxl/minidump/NOT-IN-NotNullBoth.mdp @@ -255,7 +255,7 @@ - + @@ -269,7 +269,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/NOT-IN-NullInner.mdp b/src/backend/gporca/data/dxl/minidump/NOT-IN-NullInner.mdp index 17b91238d3dc..7450ed6ac6ac 100644 --- a/src/backend/gporca/data/dxl/minidump/NOT-IN-NullInner.mdp +++ b/src/backend/gporca/data/dxl/minidump/NOT-IN-NullInner.mdp @@ -299,7 +299,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/NOT-IN-NullOuter.mdp b/src/backend/gporca/data/dxl/minidump/NOT-IN-NullOuter.mdp index c5f04180bdb3..25e3faf6c151 100644 --- a/src/backend/gporca/data/dxl/minidump/NOT-IN-NullOuter.mdp +++ b/src/backend/gporca/data/dxl/minidump/NOT-IN-NullOuter.mdp @@ -299,7 +299,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/NaryWithLojAndNonLojChilds.mdp b/src/backend/gporca/data/dxl/minidump/NaryWithLojAndNonLojChilds.mdp index 39fb3a7861d8..28222aa13c3b 100644 --- a/src/backend/gporca/data/dxl/minidump/NaryWithLojAndNonLojChilds.mdp +++ b/src/backend/gporca/data/dxl/minidump/NaryWithLojAndNonLojChilds.mdp @@ -1732,7 +1732,7 @@ - + @@ -1758,7 +1758,7 @@ - + @@ -1790,7 +1790,7 @@ - + @@ -1819,7 +1819,7 @@ - + @@ -1868,7 +1868,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Nested-Setops-2.mdp b/src/backend/gporca/data/dxl/minidump/Nested-Setops-2.mdp index 667635c48d14..8882432bbfa8 100644 --- a/src/backend/gporca/data/dxl/minidump/Nested-Setops-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/Nested-Setops-2.mdp @@ -600,7 +600,7 @@ - + @@ -614,7 +614,7 @@ - + @@ -631,7 +631,7 @@ - + @@ -650,7 +650,7 @@ - + @@ -682,7 +682,7 @@ - + @@ -695,7 +695,7 @@ - + @@ -709,7 +709,7 @@ - + @@ -722,7 +722,7 @@ - + @@ -737,7 +737,7 @@ - + @@ -753,7 +753,7 @@ - + @@ -766,7 +766,7 @@ - + @@ -777,85 +777,83 @@ - + - + - - - - - + + - - - - - - - - - + + - - - - - - - - - + + + + + + + + + - + - + - - + + + + + - - - - - - + - - + + + + + - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - + @@ -864,7 +862,7 @@ - + @@ -878,7 +876,7 @@ - + @@ -891,7 +889,7 @@ - + @@ -906,7 +904,7 @@ - + @@ -922,7 +920,7 @@ - + @@ -935,7 +933,7 @@ - + @@ -946,85 +944,83 @@ - + - + - - - - - + + - - - - - - - - - + + - - - - - - - - - + + + + + + + + + - + - + - - + + + + + - - - - - - + - - + + + + + - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - + @@ -1034,7 +1030,7 @@ - + @@ -1048,7 +1044,7 @@ - + @@ -1061,7 +1057,7 @@ - + @@ -1076,7 +1072,7 @@ - + @@ -1092,7 +1088,7 @@ - + @@ -1105,7 +1101,7 @@ - + @@ -1116,78 +1112,76 @@ - + - + - - - - - + + - + - - - - - - - - - + + + + + + + + + - + - + - - + + + + + - - - - - - + - - + + + + + - + - - - - - - - - - + + + + + + + + + - + diff --git a/src/backend/gporca/data/dxl/minidump/NestedJoinWithCastedColumn.mdp b/src/backend/gporca/data/dxl/minidump/NestedJoinWithCastedColumn.mdp index 972e80b8d468..92ce295de050 100644 --- a/src/backend/gporca/data/dxl/minidump/NestedJoinWithCastedColumn.mdp +++ b/src/backend/gporca/data/dxl/minidump/NestedJoinWithCastedColumn.mdp @@ -585,7 +585,7 @@ - + @@ -601,7 +601,7 @@ - + @@ -612,7 +612,7 @@ - + @@ -694,7 +694,7 @@ - + @@ -715,7 +715,7 @@ - + @@ -732,7 +732,7 @@ - + @@ -751,7 +751,7 @@ - + @@ -773,7 +773,7 @@ - + @@ -786,7 +786,7 @@ - + @@ -800,7 +800,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/NestedSubqLimitBindings.mdp b/src/backend/gporca/data/dxl/minidump/NestedSubqLimitBindings.mdp index 09170e7d7fd0..3ac5d9473ac2 100644 --- a/src/backend/gporca/data/dxl/minidump/NestedSubqLimitBindings.mdp +++ b/src/backend/gporca/data/dxl/minidump/NestedSubqLimitBindings.mdp @@ -484,7 +484,7 @@ select a from t as t1 where a in ( - + @@ -498,7 +498,7 @@ select a from t as t1 where a in ( - + @@ -509,7 +509,7 @@ select a from t as t1 where a in ( - + @@ -523,7 +523,7 @@ select a from t as t1 where a in ( - + @@ -533,7 +533,7 @@ select a from t as t1 where a in ( - + @@ -551,7 +551,7 @@ select a from t as t1 where a in ( - + @@ -561,7 +561,7 @@ select a from t as t1 where a in ( - + @@ -579,7 +579,7 @@ select a from t as t1 where a in ( - + @@ -589,7 +589,7 @@ select a from t as t1 where a in ( - + @@ -600,7 +600,7 @@ select a from t as t1 where a in ( - + @@ -640,7 +640,7 @@ select a from t as t1 where a in ( - + @@ -653,7 +653,7 @@ select a from t as t1 where a in ( - + @@ -668,7 +668,7 @@ select a from t as t1 where a in ( - + @@ -678,7 +678,7 @@ select a from t as t1 where a in ( - + @@ -691,7 +691,7 @@ select a from t as t1 where a in ( - + @@ -706,7 +706,7 @@ select a from t as t1 where a in ( - + @@ -772,7 +772,7 @@ select a from t as t1 where a in ( - + @@ -785,7 +785,7 @@ select a from t as t1 where a in ( - + @@ -800,7 +800,7 @@ select a from t as t1 where a in ( - + @@ -866,7 +866,7 @@ select a from t as t1 where a in ( - + @@ -879,7 +879,7 @@ select a from t as t1 where a in ( - + @@ -894,7 +894,7 @@ select a from t as t1 where a in ( - + diff --git a/src/backend/gporca/data/dxl/minidump/NoBroadcastUnderGatherForWindowFunction.mdp b/src/backend/gporca/data/dxl/minidump/NoBroadcastUnderGatherForWindowFunction.mdp index 337cb723f57e..87c0171cb14b 100644 --- a/src/backend/gporca/data/dxl/minidump/NoBroadcastUnderGatherForWindowFunction.mdp +++ b/src/backend/gporca/data/dxl/minidump/NoBroadcastUnderGatherForWindowFunction.mdp @@ -436,7 +436,7 @@ WHERE buyer_name = 'ABC' - + @@ -457,7 +457,7 @@ WHERE buyer_name = 'ABC' - + @@ -472,7 +472,7 @@ WHERE buyer_name = 'ABC' - + diff --git a/src/backend/gporca/data/dxl/minidump/NoDistKeyMultiPredJoinCardinality.mdp b/src/backend/gporca/data/dxl/minidump/NoDistKeyMultiPredJoinCardinality.mdp index d9b6d9382ee7..fb2f27dc7fd6 100644 --- a/src/backend/gporca/data/dxl/minidump/NoDistKeyMultiPredJoinCardinality.mdp +++ b/src/backend/gporca/data/dxl/minidump/NoDistKeyMultiPredJoinCardinality.mdp @@ -1587,7 +1587,7 @@ - + @@ -1613,7 +1613,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/NoPushdownPredicateWithCTEAnchor.mdp b/src/backend/gporca/data/dxl/minidump/NoPushdownPredicateWithCTEAnchor.mdp index 49f6af3e14f9..29cc995064c2 100644 --- a/src/backend/gporca/data/dxl/minidump/NoPushdownPredicateWithCTEAnchor.mdp +++ b/src/backend/gporca/data/dxl/minidump/NoPushdownPredicateWithCTEAnchor.mdp @@ -461,7 +461,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/NoRedistributeOnAppend.mdp b/src/backend/gporca/data/dxl/minidump/NoRedistributeOnAppend.mdp index a6fddc528273..8ff25f27b912 100644 --- a/src/backend/gporca/data/dxl/minidump/NoRedistributeOnAppend.mdp +++ b/src/backend/gporca/data/dxl/minidump/NoRedistributeOnAppend.mdp @@ -428,7 +428,7 @@ GROUP BY b; - + @@ -442,7 +442,7 @@ GROUP BY b; - + @@ -456,7 +456,7 @@ GROUP BY b; - + @@ -469,7 +469,7 @@ GROUP BY b; - + @@ -484,7 +484,7 @@ GROUP BY b; - + @@ -607,7 +607,7 @@ GROUP BY b; - + @@ -618,7 +618,7 @@ GROUP BY b; - + @@ -631,7 +631,7 @@ GROUP BY b; - + @@ -646,7 +646,7 @@ GROUP BY b; - + @@ -662,7 +662,7 @@ GROUP BY b; - + @@ -675,7 +675,7 @@ GROUP BY b; - + @@ -690,7 +690,7 @@ GROUP BY b; - + @@ -774,7 +774,7 @@ GROUP BY b; - + @@ -785,7 +785,7 @@ GROUP BY b; - + @@ -798,7 +798,7 @@ GROUP BY b; - + @@ -813,7 +813,7 @@ GROUP BY b; - + @@ -829,7 +829,7 @@ GROUP BY b; - + @@ -842,7 +842,7 @@ GROUP BY b; - + @@ -857,7 +857,7 @@ GROUP BY b; - + diff --git a/src/backend/gporca/data/dxl/minidump/Non-Hashjoinable-Pred-2.mdp b/src/backend/gporca/data/dxl/minidump/Non-Hashjoinable-Pred-2.mdp index 5ecb79b68f40..090416286dc1 100644 --- a/src/backend/gporca/data/dxl/minidump/Non-Hashjoinable-Pred-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/Non-Hashjoinable-Pred-2.mdp @@ -259,7 +259,7 @@ - + @@ -279,7 +279,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/NonSingleton.mdp b/src/backend/gporca/data/dxl/minidump/NonSingleton.mdp index 0bdff2c31047..0be37a5e18cf 100644 --- a/src/backend/gporca/data/dxl/minidump/NonSingleton.mdp +++ b/src/backend/gporca/data/dxl/minidump/NonSingleton.mdp @@ -285,7 +285,7 @@ EXPLAIN SELECT a FROM snackbox JOIN (SELECT c FROM hottoast LIMIT 3) hottoast ON - + @@ -296,7 +296,7 @@ EXPLAIN SELECT a FROM snackbox JOIN (SELECT c FROM hottoast LIMIT 3) hottoast ON - + diff --git a/src/backend/gporca/data/dxl/minidump/NotExists-SuperflousOuterRefWithGbAgg.mdp b/src/backend/gporca/data/dxl/minidump/NotExists-SuperflousOuterRefWithGbAgg.mdp index c8744233c4cf..24f8eb5a8a0e 100644 --- a/src/backend/gporca/data/dxl/minidump/NotExists-SuperflousOuterRefWithGbAgg.mdp +++ b/src/backend/gporca/data/dxl/minidump/NotExists-SuperflousOuterRefWithGbAgg.mdp @@ -377,7 +377,7 @@ explain select * from A where not exists (select sum(C.i) from C where C.i = A.i - + @@ -391,7 +391,7 @@ explain select * from A where not exists (select sum(C.i) from C where C.i = A.i - + diff --git a/src/backend/gporca/data/dxl/minidump/NotInToLASJ.mdp b/src/backend/gporca/data/dxl/minidump/NotInToLASJ.mdp index 98d8fcfe62f8..d5a013aed2b4 100644 --- a/src/backend/gporca/data/dxl/minidump/NotInToLASJ.mdp +++ b/src/backend/gporca/data/dxl/minidump/NotInToLASJ.mdp @@ -333,7 +333,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/OneDistKeyMultiPredJoinCardinality.mdp b/src/backend/gporca/data/dxl/minidump/OneDistKeyMultiPredJoinCardinality.mdp index 790a62dd7c90..a2af75d1ccb6 100644 --- a/src/backend/gporca/data/dxl/minidump/OneDistKeyMultiPredJoinCardinality.mdp +++ b/src/backend/gporca/data/dxl/minidump/OneDistKeyMultiPredJoinCardinality.mdp @@ -1223,7 +1223,7 @@ - + @@ -1249,7 +1249,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/OuterJoinOnViewWithCastedColumn.mdp b/src/backend/gporca/data/dxl/minidump/OuterJoinOnViewWithCastedColumn.mdp index e747979d2b5e..387bda782035 100644 --- a/src/backend/gporca/data/dxl/minidump/OuterJoinOnViewWithCastedColumn.mdp +++ b/src/backend/gporca/data/dxl/minidump/OuterJoinOnViewWithCastedColumn.mdp @@ -348,7 +348,7 @@ - + @@ -368,7 +368,7 @@ - + @@ -394,7 +394,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartSelectorOnJoinSide.mdp b/src/backend/gporca/data/dxl/minidump/PartSelectorOnJoinSide.mdp index f026aedf08b6..ad49fbbbb653 100644 --- a/src/backend/gporca/data/dxl/minidump/PartSelectorOnJoinSide.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartSelectorOnJoinSide.mdp @@ -1618,10 +1618,10 @@ - + - + @@ -1653,7 +1653,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartSelectorOnJoinSide2.mdp b/src/backend/gporca/data/dxl/minidump/PartSelectorOnJoinSide2.mdp index 4d083e86961c..c3775aa40065 100644 --- a/src/backend/gporca/data/dxl/minidump/PartSelectorOnJoinSide2.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartSelectorOnJoinSide2.mdp @@ -1621,7 +1621,7 @@ - + @@ -1635,7 +1635,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-AggWithExistentialSubquery.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-AggWithExistentialSubquery.mdp index fea5bd0a8a61..eb6e838500c5 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-AggWithExistentialSubquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-AggWithExistentialSubquery.mdp @@ -436,7 +436,7 @@ - + @@ -454,7 +454,7 @@ - + @@ -465,7 +465,7 @@ - + @@ -495,7 +495,7 @@ - + @@ -532,7 +532,7 @@ - + @@ -571,7 +571,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-AvoidRangePred-DPE.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-AvoidRangePred-DPE.mdp index c362f2d9c709..1dc7ea529136 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-AvoidRangePred-DPE.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-AvoidRangePred-DPE.mdp @@ -828,7 +828,7 @@ FROM abuela JOIN bar ON a = c AND b BETWEEN d - 1 AND d + 4 - + @@ -839,7 +839,7 @@ FROM abuela JOIN bar ON a = c AND b BETWEEN d - 1 AND d + 4 - + @@ -860,7 +860,7 @@ FROM abuela JOIN bar ON a = c AND b BETWEEN d - 1 AND d + 4 - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-GroupBy.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-GroupBy.mdp index 36be4570d0ef..1906e5c43fb8 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-GroupBy.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-GroupBy.mdp @@ -536,7 +536,7 @@ - + @@ -559,7 +559,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-Limit.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-Limit.mdp index fc00b62c6311..1ae8e2969a7b 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-Limit.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-Limit.mdp @@ -490,7 +490,7 @@ - + @@ -525,7 +525,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-PartitionSelectorRewindability.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-PartitionSelectorRewindability.mdp index 7a7f70eb4448..703acb5e3fdf 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-PartitionSelectorRewindability.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-PartitionSelectorRewindability.mdp @@ -422,7 +422,7 @@ - + @@ -433,14 +433,14 @@ - + - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-WindowFunction.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-WindowFunction.mdp index 3d21fc998224..3a358e166466 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-WindowFunction.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-DPE-WindowFunction.mdp @@ -686,7 +686,7 @@ - + @@ -706,7 +706,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-DisablePartSelectionJoin.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-DisablePartSelectionJoin.mdp index e6f118392992..787ebad097df 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-DisablePartSelectionJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-DisablePartSelectionJoin.mdp @@ -596,7 +596,7 @@ - + @@ -616,7 +616,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-HJ3.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-HJ3.mdp index 77791e4bca32..fc54bea2ff33 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-HJ3.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-HJ3.mdp @@ -219,7 +219,7 @@ - + @@ -239,7 +239,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-HJ4.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-HJ4.mdp index 0790e1108f71..d7ab0e27b884 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-HJ4.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-HJ4.mdp @@ -273,7 +273,7 @@ - + @@ -287,7 +287,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-HJ5.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-HJ5.mdp index fd4e4b61cf8e..b6294768d849 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-HJ5.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-HJ5.mdp @@ -294,7 +294,7 @@ select * from s,t where s.b=t.a and t.b=35; - + @@ -314,7 +314,7 @@ select * from s,t where s.b=t.a and t.b=35; - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverExcept.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverExcept.mdp index 3d9b69fc2f59..191d9ac57444 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverExcept.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverExcept.mdp @@ -1284,10 +1284,10 @@ select * from (select * from p1 except select * from p2) as p, tt where p.b=tt.b - + - + @@ -1307,7 +1307,7 @@ select * from (select * from p1 except select * from p2) as p, tt where p.b=tt.b - + @@ -1333,7 +1333,7 @@ select * from (select * from p1 except select * from p2) as p, tt where p.b=tt.b - + @@ -1350,7 +1350,7 @@ select * from (select * from p1 except select * from p2) as p, tt where p.b=tt.b - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverGbAgg-2.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverGbAgg-2.mdp index ed0ba9e21a57..68d28872178b 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverGbAgg-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverGbAgg-2.mdp @@ -1237,7 +1237,7 @@ - + @@ -1260,7 +1260,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverGbAgg.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverGbAgg.mdp index 2ef4d47f8bea..1d817faa8121 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverGbAgg.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverGbAgg.mdp @@ -298,7 +298,7 @@ - + @@ -318,7 +318,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverIntersect.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverIntersect.mdp index 0f645f52d258..a77c9f00dca0 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverIntersect.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverIntersect.mdp @@ -1284,10 +1284,10 @@ select * from (select * from p1 intersect select * from p2) as p, tt where p.b=t - + - + @@ -1307,7 +1307,7 @@ select * from (select * from p1 intersect select * from p2) as p, tt where p.b=t - + @@ -1333,7 +1333,7 @@ select * from (select * from p1 intersect select * from p2) as p, tt where p.b=t - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverUnion-1.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverUnion-1.mdp index c51138edb6dc..36508f599c19 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverUnion-1.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverUnion-1.mdp @@ -1287,7 +1287,7 @@ select * from (select * from p1 union all select * from p2) as p, tt where p.b=t - + @@ -1307,7 +1307,7 @@ select * from (select * from p1 union all select * from p2) as p, tt where p.b=t - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverUnion-2.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverUnion-2.mdp index 6fb3eebdb2a3..33a7a6e03025 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverUnion-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-JoinOverUnion-2.mdp @@ -888,7 +888,7 @@ select * from (select * from tt union all select * from p2) as p, tt where p.b=t - + @@ -908,7 +908,7 @@ select * from (select * from tt union all select * from p2) as p, tt where p.b=t - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-LASJ.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-LASJ.mdp index 6a957bfd50ba..45e36dae5f43 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-LASJ.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-LASJ.mdp @@ -642,7 +642,7 @@ - + @@ -660,7 +660,7 @@ - + @@ -671,7 +671,7 @@ - + @@ -687,7 +687,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-LeftOuterHashJoin-DPE-IsNull.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-LeftOuterHashJoin-DPE-IsNull.mdp index de06e964bd33..d9189725b7b6 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-LeftOuterHashJoin-DPE-IsNull.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-LeftOuterHashJoin-DPE-IsNull.mdp @@ -269,7 +269,7 @@ select * from t2 left outer join t1 on t1.b = t2.d where t1.b is null; - + @@ -289,7 +289,7 @@ select * from t2 left outer join t1 on t1.b = t2.d where t1.b is null; - + @@ -313,7 +313,7 @@ select * from t2 left outer join t1 on t1.b = t2.d where t1.b is null; - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoin.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoin.mdp index 6810bf1806ba..5eb2cf50016b 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoin.mdp @@ -415,7 +415,7 @@ explain select count(*) from r_p, s c, s d, s e where r_p.a = c.c and r_p.a = d. - + @@ -433,7 +433,7 @@ explain select count(*) from r_p, s c, s d, s e where r_p.a = c.c and r_p.a = d. - + @@ -444,7 +444,7 @@ explain select count(*) from r_p, s c, s d, s e where r_p.a = c.c and r_p.a = d. - + @@ -460,7 +460,7 @@ explain select count(*) from r_p, s c, s d, s e where r_p.a = c.c and r_p.a = d. - + @@ -473,7 +473,7 @@ explain select count(*) from r_p, s c, s d, s e where r_p.a = c.c and r_p.a = d. - + @@ -571,7 +571,7 @@ explain select count(*) from r_p, s c, s d, s e where r_p.a = c.c and r_p.a = d. - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoinWithDPE-2.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoinWithDPE-2.mdp index 780036b48b2a..339ba3ddedde 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoinWithDPE-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoinWithDPE-2.mdp @@ -1352,7 +1352,7 @@ - + @@ -1513,7 +1513,7 @@ - + @@ -1901,7 +1901,7 @@ - + @@ -1993,7 +1993,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoinWithDPE.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoinWithDPE.mdp index be0129d522a9..45c551109ac9 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoinWithDPE.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-MultiWayJoinWithDPE.mdp @@ -3258,7 +3258,7 @@ - + @@ -3484,13 +3484,109 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3699,7 +3795,7 @@ - + @@ -3914,7 +4010,7 @@ - + @@ -4726,102 +4822,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-RangeJoinPred.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-RangeJoinPred.mdp index 6230a79b3db4..762a0e2967ab 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-RangeJoinPred.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-RangeJoinPred.mdp @@ -5170,7 +5170,7 @@ where d.msisdn=f.subscriberaddress and f.sessioncreationtimestamp >= d.start_dtm - + @@ -5241,7 +5241,7 @@ where d.msisdn=f.subscriberaddress and f.sessioncreationtimestamp >= d.start_dtm - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-RightOuterHashJoin-DPE-IsNull.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-RightOuterHashJoin-DPE-IsNull.mdp index 1d2a5cc5c746..67c2d97984a7 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-RightOuterHashJoin-DPE-IsNull.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-RightOuterHashJoin-DPE-IsNull.mdp @@ -306,7 +306,7 @@ - + @@ -330,7 +330,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-SQAny.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-SQAny.mdp index 139efeadd84e..c6f5d1a415b0 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-SQAny.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-SQAny.mdp @@ -359,7 +359,7 @@ - + @@ -373,7 +373,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-SQExists.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-SQExists.mdp index cda1040b510b..6f0f5934888c 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-SQExists.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-SQExists.mdp @@ -312,7 +312,7 @@ - + @@ -326,7 +326,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-SQNotExists.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-SQNotExists.mdp index 01cea25438b0..5f397c7c0269 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-SQNotExists.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-SQNotExists.mdp @@ -306,7 +306,7 @@ - + @@ -320,7 +320,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-SQScalar.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-SQScalar.mdp index 84a51c16443e..e7efd1128416 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-SQScalar.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-SQScalar.mdp @@ -322,7 +322,7 @@ - + @@ -336,7 +336,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-WindowFunction.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-WindowFunction.mdp index 5fc1411387df..75df46c91448 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-WindowFunction.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-WindowFunction.mdp @@ -686,7 +686,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-11.mdp b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-11.mdp index 3c44d6e4a231..7d521933ed13 100644 --- a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-11.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-11.mdp @@ -323,7 +323,7 @@ - + @@ -337,7 +337,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-12.mdp b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-12.mdp index 627f4dfa7f90..32f8a7466e73 100644 --- a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-12.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-12.mdp @@ -297,7 +297,7 @@ - + @@ -311,7 +311,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-13.mdp b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-13.mdp index 1b37997ebfd8..5a757197a70f 100644 --- a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-13.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-13.mdp @@ -334,7 +334,7 @@ - + @@ -348,7 +348,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-14.mdp b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-14.mdp index 96529708fe88..9d3ac2bf2b0a 100644 --- a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-14.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-14.mdp @@ -381,7 +381,7 @@ - + @@ -395,7 +395,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-15.mdp b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-15.mdp index 7f06a97a9184..377138d63fe4 100644 --- a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-15.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-15.mdp @@ -381,7 +381,7 @@ - + @@ -395,7 +395,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-2.mdp b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-2.mdp index 08ae782e28e3..0ceb241f222c 100644 --- a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-2.mdp @@ -418,7 +418,7 @@ - + @@ -434,14 +434,14 @@ - + - + diff --git a/src/backend/gporca/data/dxl/minidump/PushFilterToSemiJoinLeftChild.mdp b/src/backend/gporca/data/dxl/minidump/PushFilterToSemiJoinLeftChild.mdp index 210e626f630c..d4343715a1d5 100644 --- a/src/backend/gporca/data/dxl/minidump/PushFilterToSemiJoinLeftChild.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushFilterToSemiJoinLeftChild.mdp @@ -311,7 +311,7 @@ - + @@ -322,7 +322,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PushGbBelowJoin-NegativeCase.mdp b/src/backend/gporca/data/dxl/minidump/PushGbBelowJoin-NegativeCase.mdp index 98c079805bfe..b75f3662435d 100644 --- a/src/backend/gporca/data/dxl/minidump/PushGbBelowJoin-NegativeCase.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushGbBelowJoin-NegativeCase.mdp @@ -409,7 +409,7 @@ - + @@ -420,7 +420,7 @@ - + @@ -437,7 +437,7 @@ - + @@ -456,7 +456,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PushSelectDownUnionAllOfCTG.mdp b/src/backend/gporca/data/dxl/minidump/PushSelectDownUnionAllOfCTG.mdp index 86e78954ee23..5ebc48e7d0b1 100644 --- a/src/backend/gporca/data/dxl/minidump/PushSelectDownUnionAllOfCTG.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushSelectDownUnionAllOfCTG.mdp @@ -217,7 +217,7 @@ - + @@ -235,7 +235,7 @@ - + @@ -245,7 +245,7 @@ - + @@ -400,7 +400,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Remove-Distinct-From-Subquery.mdp b/src/backend/gporca/data/dxl/minidump/Remove-Distinct-From-Subquery.mdp index 2d4d8603d2b0..6c167a2fea41 100644 --- a/src/backend/gporca/data/dxl/minidump/Remove-Distinct-From-Subquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/Remove-Distinct-From-Subquery.mdp @@ -524,7 +524,7 @@ select * from bar where c not in (select distinct a from foo); - + @@ -538,7 +538,7 @@ select * from bar where c not in (select distinct a from foo); - + @@ -649,7 +649,7 @@ select * from bar where c not in (select distinct a from foo); - + @@ -828,7 +828,7 @@ select * from bar where c not in (select distinct a from foo); - + @@ -842,7 +842,7 @@ select * from bar where c not in (select distinct a from foo); - + diff --git a/src/backend/gporca/data/dxl/minidump/RemoveImpliedPredOnBCCPredicates.mdp b/src/backend/gporca/data/dxl/minidump/RemoveImpliedPredOnBCCPredicates.mdp index 4451ac16e8b2..7a9002c18df0 100644 --- a/src/backend/gporca/data/dxl/minidump/RemoveImpliedPredOnBCCPredicates.mdp +++ b/src/backend/gporca/data/dxl/minidump/RemoveImpliedPredOnBCCPredicates.mdp @@ -278,7 +278,7 @@ - + @@ -304,7 +304,7 @@ - + @@ -368,7 +368,7 @@ - + @@ -404,7 +404,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedHashJoinReplicated.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedHashJoinReplicated.mdp index 121d74dd666d..e56d0d69112a 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedHashJoinReplicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedHashJoinReplicated.mdp @@ -1046,7 +1046,7 @@ - + @@ -1066,7 +1066,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedJoinHashDistributedTable.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedJoinHashDistributedTable.mdp index a609fadbb521..16926488e209 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedJoinHashDistributedTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedJoinHashDistributedTable.mdp @@ -1440,7 +1440,7 @@ - + @@ -1460,7 +1460,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedJoinPartitionedTable.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedJoinPartitionedTable.mdp index dbc4893bec8e..539f55d74f53 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedJoinPartitionedTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedJoinPartitionedTable.mdp @@ -652,7 +652,7 @@ - + @@ -672,7 +672,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedJoinRandomDistributedTable.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedJoinRandomDistributedTable.mdp index 48f7d8883ada..dffb62bcae8b 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedJoinRandomDistributedTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedJoinRandomDistributedTable.mdp @@ -1042,7 +1042,7 @@ - + @@ -1062,7 +1062,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedLOJHashDistributedTable.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedLOJHashDistributedTable.mdp index 6448db1a7257..8e5f26a585f1 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedLOJHashDistributedTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedLOJHashDistributedTable.mdp @@ -1440,7 +1440,7 @@ - + @@ -1460,7 +1460,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedLOJRandomDistributedTable.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedLOJRandomDistributedTable.mdp index 5e35b86a516b..cd7e26e78e84 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedLOJRandomDistributedTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedLOJRandomDistributedTable.mdp @@ -1042,7 +1042,7 @@ - + @@ -1062,7 +1062,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedLOJReplicated.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedLOJReplicated.mdp index e38e97961353..e9e5ae1ee503 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedLOJReplicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedLOJReplicated.mdp @@ -1046,7 +1046,7 @@ - + @@ -1066,7 +1066,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedTableCTE.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedTableCTE.mdp index 11f568fac780..ed134c981484 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedTableCTE.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedTableCTE.mdp @@ -1446,7 +1446,7 @@ - + @@ -1466,7 +1466,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedTableInClause.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedTableInClause.mdp index 05d82731ca16..5f724ae1ab3f 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedTableInClause.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedTableInClause.mdp @@ -1108,7 +1108,7 @@ - + @@ -1122,7 +1122,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/RightJoinBothReplicated.mdp b/src/backend/gporca/data/dxl/minidump/RightJoinBothReplicated.mdp index d38955682580..f2b8a47c450e 100644 --- a/src/backend/gporca/data/dxl/minidump/RightJoinBothReplicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/RightJoinBothReplicated.mdp @@ -715,7 +715,7 @@ - + @@ -735,7 +735,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/RightJoinDPS.mdp b/src/backend/gporca/data/dxl/minidump/RightJoinDPS.mdp index e411f82d9092..535db0a37767 100644 --- a/src/backend/gporca/data/dxl/minidump/RightJoinDPS.mdp +++ b/src/backend/gporca/data/dxl/minidump/RightJoinDPS.mdp @@ -357,7 +357,7 @@ - + @@ -377,7 +377,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/RightJoinHashed.mdp b/src/backend/gporca/data/dxl/minidump/RightJoinHashed.mdp index c59ebdf7b3d9..95f00be781d2 100644 --- a/src/backend/gporca/data/dxl/minidump/RightJoinHashed.mdp +++ b/src/backend/gporca/data/dxl/minidump/RightJoinHashed.mdp @@ -719,7 +719,7 @@ - + @@ -739,7 +739,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/RightJoinNoDPSNonDistKey.mdp b/src/backend/gporca/data/dxl/minidump/RightJoinNoDPSNonDistKey.mdp index 0c966d659373..17ead5e060f8 100644 --- a/src/backend/gporca/data/dxl/minidump/RightJoinNoDPSNonDistKey.mdp +++ b/src/backend/gporca/data/dxl/minidump/RightJoinNoDPSNonDistKey.mdp @@ -762,7 +762,7 @@ - + @@ -782,7 +782,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/RightJoinRedistribute.mdp b/src/backend/gporca/data/dxl/minidump/RightJoinRedistribute.mdp index 74f9dd565031..1d650259a91a 100644 --- a/src/backend/gporca/data/dxl/minidump/RightJoinRedistribute.mdp +++ b/src/backend/gporca/data/dxl/minidump/RightJoinRedistribute.mdp @@ -763,7 +763,7 @@ - + @@ -783,7 +783,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/RightJoinReplicated.mdp b/src/backend/gporca/data/dxl/minidump/RightJoinReplicated.mdp index 69b37f154a0a..964aff6d630a 100644 --- a/src/backend/gporca/data/dxl/minidump/RightJoinReplicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/RightJoinReplicated.mdp @@ -1076,7 +1076,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/RightJoinTVF.mdp b/src/backend/gporca/data/dxl/minidump/RightJoinTVF.mdp index c30375a2e185..bfb7af1717f2 100644 --- a/src/backend/gporca/data/dxl/minidump/RightJoinTVF.mdp +++ b/src/backend/gporca/data/dxl/minidump/RightJoinTVF.mdp @@ -320,7 +320,7 @@ - + @@ -331,7 +331,7 @@ - + @@ -344,7 +344,7 @@ - + @@ -359,7 +359,7 @@ - + @@ -375,7 +375,7 @@ - + @@ -388,7 +388,7 @@ - + @@ -399,7 +399,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Select-Over-CTEAnchor.mdp b/src/backend/gporca/data/dxl/minidump/Select-Over-CTEAnchor.mdp index 46e73e658944..625ae11670dd 100644 --- a/src/backend/gporca/data/dxl/minidump/Select-Over-CTEAnchor.mdp +++ b/src/backend/gporca/data/dxl/minidump/Select-Over-CTEAnchor.mdp @@ -463,7 +463,7 @@ - + @@ -489,7 +489,7 @@ - + @@ -520,7 +520,7 @@ - + @@ -540,7 +540,7 @@ - + @@ -558,7 +558,7 @@ - + @@ -576,7 +576,7 @@ - + @@ -663,7 +663,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Select-Proj-OuterJoin.mdp b/src/backend/gporca/data/dxl/minidump/Select-Proj-OuterJoin.mdp index bd679820b58e..8c1b163b2004 100644 --- a/src/backend/gporca/data/dxl/minidump/Select-Proj-OuterJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/Select-Proj-OuterJoin.mdp @@ -892,7 +892,7 @@ - + @@ -905,7 +905,7 @@ - + @@ -920,7 +920,7 @@ - + @@ -937,7 +937,7 @@ - + @@ -951,7 +951,7 @@ - + @@ -999,7 +999,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/SemiJoin2InnerJoin.mdp b/src/backend/gporca/data/dxl/minidump/SemiJoin2InnerJoin.mdp index 7c84a45fcaf3..349689a49e0d 100644 --- a/src/backend/gporca/data/dxl/minidump/SemiJoin2InnerJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/SemiJoin2InnerJoin.mdp @@ -531,7 +531,7 @@ - + @@ -542,7 +542,7 @@ - + @@ -553,7 +553,7 @@ - + @@ -574,7 +574,7 @@ - + @@ -596,7 +596,7 @@ - + @@ -621,7 +621,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/SixWayDPv2.mdp b/src/backend/gporca/data/dxl/minidump/SixWayDPv2.mdp index aa39255a2117..32f24260e28f 100644 --- a/src/backend/gporca/data/dxl/minidump/SixWayDPv2.mdp +++ b/src/backend/gporca/data/dxl/minidump/SixWayDPv2.mdp @@ -495,7 +495,7 @@ - + @@ -539,7 +539,7 @@ - + @@ -589,7 +589,7 @@ - + @@ -633,7 +633,7 @@ - + @@ -670,7 +670,7 @@ - + @@ -708,7 +708,7 @@ - + @@ -739,7 +739,7 @@ - + @@ -771,7 +771,7 @@ - + @@ -796,7 +796,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/SpoolShouldInvalidateUnresolvedDynamicScans.mdp b/src/backend/gporca/data/dxl/minidump/SpoolShouldInvalidateUnresolvedDynamicScans.mdp index 1857ddef1ea9..e5b65ca47d34 100644 --- a/src/backend/gporca/data/dxl/minidump/SpoolShouldInvalidateUnresolvedDynamicScans.mdp +++ b/src/backend/gporca/data/dxl/minidump/SpoolShouldInvalidateUnresolvedDynamicScans.mdp @@ -608,7 +608,7 @@ And instead have a plan like - + @@ -634,7 +634,7 @@ And instead have a plan like - + @@ -707,7 +707,7 @@ And instead have a plan like - + @@ -726,7 +726,7 @@ And instead have a plan like - + diff --git a/src/backend/gporca/data/dxl/minidump/SqlFuncNullReject.mdp b/src/backend/gporca/data/dxl/minidump/SqlFuncNullReject.mdp index 2bd402640de5..5f9d2a174c87 100644 --- a/src/backend/gporca/data/dxl/minidump/SqlFuncNullReject.mdp +++ b/src/backend/gporca/data/dxl/minidump/SqlFuncNullReject.mdp @@ -287,14 +287,14 @@ yolo(c) = 16 - + - + diff --git a/src/backend/gporca/data/dxl/minidump/SqlFuncPredFactorize.mdp b/src/backend/gporca/data/dxl/minidump/SqlFuncPredFactorize.mdp index d3d9c870237c..b24a3be6b9e9 100644 --- a/src/backend/gporca/data/dxl/minidump/SqlFuncPredFactorize.mdp +++ b/src/backend/gporca/data/dxl/minidump/SqlFuncPredFactorize.mdp @@ -322,14 +322,14 @@ WHERE a = c AND - + - + diff --git a/src/backend/gporca/data/dxl/minidump/Stat-Derivation-Leaf-Pattern.mdp b/src/backend/gporca/data/dxl/minidump/Stat-Derivation-Leaf-Pattern.mdp index 422c085befc2..012cec4c418b 100644 --- a/src/backend/gporca/data/dxl/minidump/Stat-Derivation-Leaf-Pattern.mdp +++ b/src/backend/gporca/data/dxl/minidump/Stat-Derivation-Leaf-Pattern.mdp @@ -830,7 +830,7 @@ AND - + diff --git a/src/backend/gporca/data/dxl/minidump/Subq-JoinWithOuterRef.mdp b/src/backend/gporca/data/dxl/minidump/Subq-JoinWithOuterRef.mdp index 2fadb34797dc..4d0b495edefe 100644 --- a/src/backend/gporca/data/dxl/minidump/Subq-JoinWithOuterRef.mdp +++ b/src/backend/gporca/data/dxl/minidump/Subq-JoinWithOuterRef.mdp @@ -420,7 +420,7 @@ - + @@ -431,7 +431,7 @@ - + @@ -442,7 +442,7 @@ - + @@ -462,7 +462,7 @@ - + @@ -478,7 +478,7 @@ - + @@ -488,7 +488,7 @@ - + @@ -499,7 +499,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Subq-On-OuterRef.mdp b/src/backend/gporca/data/dxl/minidump/Subq-On-OuterRef.mdp index 73b8dce74337..f32125b252aa 100644 --- a/src/backend/gporca/data/dxl/minidump/Subq-On-OuterRef.mdp +++ b/src/backend/gporca/data/dxl/minidump/Subq-On-OuterRef.mdp @@ -265,7 +265,7 @@ - + @@ -279,7 +279,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Subq-With-OuterRefCol.mdp b/src/backend/gporca/data/dxl/minidump/Subq-With-OuterRefCol.mdp index c4439f83c4a8..5513a712a259 100644 --- a/src/backend/gporca/data/dxl/minidump/Subq-With-OuterRefCol.mdp +++ b/src/backend/gporca/data/dxl/minidump/Subq-With-OuterRefCol.mdp @@ -535,7 +535,7 @@ pivotal=# explain select a, c from r, s where a in (select c from r) order by a, - + @@ -547,7 +547,7 @@ pivotal=# explain select a, c from r, s where a in (select c from r) order by a, - + @@ -564,7 +564,7 @@ pivotal=# explain select a, c from r, s where a in (select c from r) order by a, - + @@ -576,7 +576,7 @@ pivotal=# explain select a, c from r, s where a in (select c from r) order by a, - + @@ -595,7 +595,7 @@ pivotal=# explain select a, c from r, s where a in (select c from r) order by a, - + diff --git a/src/backend/gporca/data/dxl/minidump/Subq2CorrSQInLOJOn.mdp b/src/backend/gporca/data/dxl/minidump/Subq2CorrSQInLOJOn.mdp index 6b1a44d50399..4fcaefa3ba6e 100644 --- a/src/backend/gporca/data/dxl/minidump/Subq2CorrSQInLOJOn.mdp +++ b/src/backend/gporca/data/dxl/minidump/Subq2CorrSQInLOJOn.mdp @@ -2433,7 +2433,7 @@ - + @@ -2447,7 +2447,7 @@ - + @@ -2470,7 +2470,7 @@ - + @@ -2489,7 +2489,7 @@ - + @@ -2502,7 +2502,7 @@ - + @@ -2516,7 +2516,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Subq2NotInWhereLOJ.mdp b/src/backend/gporca/data/dxl/minidump/Subq2NotInWhereLOJ.mdp index 49f4e8c49f56..b06040459638 100644 --- a/src/backend/gporca/data/dxl/minidump/Subq2NotInWhereLOJ.mdp +++ b/src/backend/gporca/data/dxl/minidump/Subq2NotInWhereLOJ.mdp @@ -739,7 +739,7 @@ - + @@ -756,7 +756,7 @@ - + @@ -777,7 +777,7 @@ - + @@ -800,7 +800,7 @@ - + @@ -828,7 +828,7 @@ - + @@ -849,7 +849,7 @@ - + @@ -872,7 +872,7 @@ - + @@ -892,7 +892,7 @@ - + @@ -909,7 +909,7 @@ - + @@ -936,7 +936,7 @@ - + @@ -984,7 +984,7 @@ - + @@ -1011,7 +1011,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/SubqEnforceSubplan.mdp b/src/backend/gporca/data/dxl/minidump/SubqEnforceSubplan.mdp index af568b0cbc4e..933815a1763a 100644 --- a/src/backend/gporca/data/dxl/minidump/SubqEnforceSubplan.mdp +++ b/src/backend/gporca/data/dxl/minidump/SubqEnforceSubplan.mdp @@ -276,7 +276,7 @@ explain select * from foo,bar where b = (select min(b) from bar where a = b); - + diff --git a/src/backend/gporca/data/dxl/minidump/SubqExists-With-External-Corrs.mdp b/src/backend/gporca/data/dxl/minidump/SubqExists-With-External-Corrs.mdp index c102ea887f9d..32c1eedbc1e3 100644 --- a/src/backend/gporca/data/dxl/minidump/SubqExists-With-External-Corrs.mdp +++ b/src/backend/gporca/data/dxl/minidump/SubqExists-With-External-Corrs.mdp @@ -459,7 +459,7 @@ - + @@ -521,7 +521,7 @@ - + @@ -534,7 +534,7 @@ - + @@ -549,7 +549,7 @@ - + @@ -613,7 +613,7 @@ - + @@ -624,7 +624,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/SubqExists-Without-External-Corrs.mdp b/src/backend/gporca/data/dxl/minidump/SubqExists-Without-External-Corrs.mdp index 4b3514f9ba7b..bffbb71ccf27 100644 --- a/src/backend/gporca/data/dxl/minidump/SubqExists-Without-External-Corrs.mdp +++ b/src/backend/gporca/data/dxl/minidump/SubqExists-Without-External-Corrs.mdp @@ -434,7 +434,7 @@ - + @@ -448,7 +448,7 @@ - + @@ -491,32 +491,32 @@ - + - + - + - + - + @@ -553,7 +553,7 @@ - + @@ -564,7 +564,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/SubqInIndexPred.mdp b/src/backend/gporca/data/dxl/minidump/SubqInIndexPred.mdp index e1121b3b0d47..95760bbe246f 100644 --- a/src/backend/gporca/data/dxl/minidump/SubqInIndexPred.mdp +++ b/src/backend/gporca/data/dxl/minidump/SubqInIndexPred.mdp @@ -523,7 +523,7 @@ ON a = c - + @@ -534,7 +534,7 @@ ON a = c - + @@ -548,7 +548,7 @@ ON a = c - + @@ -561,7 +561,7 @@ ON a = c - + @@ -574,7 +574,7 @@ ON a = c - + diff --git a/src/backend/gporca/data/dxl/minidump/Switch-With-Subquery.mdp b/src/backend/gporca/data/dxl/minidump/Switch-With-Subquery.mdp index 3bf60d6d2251..ddd1e0447020 100644 --- a/src/backend/gporca/data/dxl/minidump/Switch-With-Subquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/Switch-With-Subquery.mdp @@ -409,7 +409,7 @@ - + @@ -422,7 +422,7 @@ - + @@ -437,7 +437,7 @@ - + @@ -458,7 +458,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/TPCDS-39-InnerJoin-JoinEstimate.mdp b/src/backend/gporca/data/dxl/minidump/TPCDS-39-InnerJoin-JoinEstimate.mdp index 474b732b0ccb..5e8a7a137050 100644 --- a/src/backend/gporca/data/dxl/minidump/TPCDS-39-InnerJoin-JoinEstimate.mdp +++ b/src/backend/gporca/data/dxl/minidump/TPCDS-39-InnerJoin-JoinEstimate.mdp @@ -2651,7 +2651,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/TPCH-Partitioned-256GB.mdp b/src/backend/gporca/data/dxl/minidump/TPCH-Partitioned-256GB.mdp index edaf7061cd5a..a348cc4bae26 100644 --- a/src/backend/gporca/data/dxl/minidump/TPCH-Partitioned-256GB.mdp +++ b/src/backend/gporca/data/dxl/minidump/TPCH-Partitioned-256GB.mdp @@ -3676,7 +3676,7 @@ - + @@ -3795,7 +3795,7 @@ - + @@ -3920,7 +3920,7 @@ - + @@ -4023,7 +4023,7 @@ - + @@ -4127,7 +4127,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/TPCH-Q5.mdp b/src/backend/gporca/data/dxl/minidump/TPCH-Q5.mdp index f1db9db09c80..81419e9f8e9e 100644 --- a/src/backend/gporca/data/dxl/minidump/TPCH-Q5.mdp +++ b/src/backend/gporca/data/dxl/minidump/TPCH-Q5.mdp @@ -6505,7 +6505,7 @@ - + @@ -6521,7 +6521,7 @@ - + @@ -6539,7 +6539,7 @@ - + @@ -6562,7 +6562,7 @@ - + @@ -6581,7 +6581,7 @@ - + @@ -6595,7 +6595,7 @@ - + @@ -6604,13 +6604,13 @@ - - - - - + + + + + + - @@ -6624,7 +6624,7 @@ - + @@ -6641,21 +6641,24 @@ - - + + - + - + - - + + + + + @@ -6663,9 +6666,6 @@ - - - @@ -6674,8 +6674,8 @@ - - + + @@ -6713,16 +6713,16 @@ - + - + - - + + - - + + @@ -6730,16 +6730,21 @@ + + + + + - + - - + + - - + + @@ -6749,54 +6754,122 @@ - - + + - + - + - - + + - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + - - + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -6807,16 +6880,10 @@ - - - - - - - - + + - + @@ -6825,199 +6892,135 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - + + - + - - + + + + + + + + - - - - - - - + + - - - - - - - - - + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + - - + + - - - - - - + - + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/TaintedReplicatedTablesCTE.mdp b/src/backend/gporca/data/dxl/minidump/TaintedReplicatedTablesCTE.mdp index e0f7c2267eda..804792b594db 100644 --- a/src/backend/gporca/data/dxl/minidump/TaintedReplicatedTablesCTE.mdp +++ b/src/backend/gporca/data/dxl/minidump/TaintedReplicatedTablesCTE.mdp @@ -417,7 +417,7 @@ - + @@ -522,7 +522,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/TimeStamp-Date-HashJoin.mdp b/src/backend/gporca/data/dxl/minidump/TimeStamp-Date-HashJoin.mdp index 299cd050713c..64b58ff6d337 100644 --- a/src/backend/gporca/data/dxl/minidump/TimeStamp-Date-HashJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/TimeStamp-Date-HashJoin.mdp @@ -294,7 +294,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Tpcds-NonPart-Q70a.mdp b/src/backend/gporca/data/dxl/minidump/Tpcds-NonPart-Q70a.mdp index 11e441c38170..5b996a03a46c 100644 --- a/src/backend/gporca/data/dxl/minidump/Tpcds-NonPart-Q70a.mdp +++ b/src/backend/gporca/data/dxl/minidump/Tpcds-NonPart-Q70a.mdp @@ -14890,7 +14890,7 @@ with results as - + @@ -14913,7 +14913,7 @@ with results as - + @@ -14943,7 +14943,7 @@ with results as - + @@ -14975,7 +14975,7 @@ with results as - + @@ -14999,7 +14999,7 @@ with results as - + @@ -15020,7 +15020,7 @@ with results as - + @@ -15043,7 +15043,7 @@ with results as - + @@ -15070,7 +15070,7 @@ with results as - + @@ -15092,7 +15092,7 @@ with results as - + @@ -15117,7 +15117,7 @@ with results as - + @@ -15134,7 +15134,7 @@ with results as - + @@ -15161,7 +15161,7 @@ with results as - + @@ -15184,7 +15184,7 @@ with results as - + @@ -15279,7 +15279,7 @@ with results as - + @@ -15296,7 +15296,7 @@ with results as - + @@ -15350,7 +15350,7 @@ with results as - + @@ -15361,7 +15361,7 @@ with results as - + @@ -15377,7 +15377,7 @@ with results as - + @@ -15393,7 +15393,7 @@ with results as - + @@ -15412,7 +15412,7 @@ with results as - + @@ -15435,7 +15435,7 @@ with results as - + @@ -15453,7 +15453,7 @@ with results as - + @@ -15472,7 +15472,7 @@ with results as - + @@ -15486,7 +15486,7 @@ with results as - + @@ -15509,7 +15509,7 @@ with results as - + @@ -15529,7 +15529,7 @@ with results as - + diff --git a/src/backend/gporca/data/dxl/minidump/TranslateFilterDisjunctQuals.mdp b/src/backend/gporca/data/dxl/minidump/TranslateFilterDisjunctQuals.mdp index 0e7897bc0f07..d58d5e3a1bda 100644 --- a/src/backend/gporca/data/dxl/minidump/TranslateFilterDisjunctQuals.mdp +++ b/src/backend/gporca/data/dxl/minidump/TranslateFilterDisjunctQuals.mdp @@ -387,7 +387,7 @@ - + @@ -398,7 +398,7 @@ - + @@ -500,7 +500,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/TranslateFilterWithCTEAndTableScanIntoFilterAndOneTimeFilter.mdp b/src/backend/gporca/data/dxl/minidump/TranslateFilterWithCTEAndTableScanIntoFilterAndOneTimeFilter.mdp index 1540265f8878..dcdeec33935d 100644 --- a/src/backend/gporca/data/dxl/minidump/TranslateFilterWithCTEAndTableScanIntoFilterAndOneTimeFilter.mdp +++ b/src/backend/gporca/data/dxl/minidump/TranslateFilterWithCTEAndTableScanIntoFilterAndOneTimeFilter.mdp @@ -615,7 +615,7 @@ - + @@ -632,7 +632,7 @@ - + @@ -647,7 +647,7 @@ - + @@ -659,7 +659,7 @@ - + @@ -731,7 +731,7 @@ - + @@ -973,7 +973,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/TranslateOneTimeFilterConjunctQuals.mdp b/src/backend/gporca/data/dxl/minidump/TranslateOneTimeFilterConjunctQuals.mdp index 7889c3640fae..2c6163cd74f0 100644 --- a/src/backend/gporca/data/dxl/minidump/TranslateOneTimeFilterConjunctQuals.mdp +++ b/src/backend/gporca/data/dxl/minidump/TranslateOneTimeFilterConjunctQuals.mdp @@ -390,7 +390,7 @@ - + @@ -401,7 +401,7 @@ - + @@ -504,7 +504,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Union-NOT-Plus-OR-Constraint.mdp b/src/backend/gporca/data/dxl/minidump/Union-NOT-Plus-OR-Constraint.mdp index 24817bc032ea..3ff44bfb751f 100644 --- a/src/backend/gporca/data/dxl/minidump/Union-NOT-Plus-OR-Constraint.mdp +++ b/src/backend/gporca/data/dxl/minidump/Union-NOT-Plus-OR-Constraint.mdp @@ -291,7 +291,7 @@ explain SELECT a.f3 FROM tb a JOIN tb b ON a.f1 = b.f1 AND NOT ( a.f2 = 0 OR a.f - + @@ -302,7 +302,7 @@ explain SELECT a.f3 FROM tb a JOIN tb b ON a.f1 = b.f1 AND NOT ( a.f2 = 0 OR a.f - + @@ -315,7 +315,7 @@ explain SELECT a.f3 FROM tb a JOIN tb b ON a.f1 = b.f1 AND NOT ( a.f2 = 0 OR a.f - + @@ -330,7 +330,7 @@ explain SELECT a.f3 FROM tb a JOIN tb b ON a.f1 = b.f1 AND NOT ( a.f2 = 0 OR a.f - + @@ -346,7 +346,7 @@ explain SELECT a.f3 FROM tb a JOIN tb b ON a.f1 = b.f1 AND NOT ( a.f2 = 0 OR a.f - + @@ -359,7 +359,7 @@ explain SELECT a.f3 FROM tb a JOIN tb b ON a.f1 = b.f1 AND NOT ( a.f2 = 0 OR a.f - + @@ -374,7 +374,7 @@ explain SELECT a.f3 FROM tb a JOIN tb b ON a.f1 = b.f1 AND NOT ( a.f2 = 0 OR a.f - + @@ -384,7 +384,7 @@ explain SELECT a.f3 FROM tb a JOIN tb b ON a.f1 = b.f1 AND NOT ( a.f2 = 0 OR a.f - + @@ -395,10 +395,50 @@ explain SELECT a.f3 FROM tb a JOIN tb b ON a.f1 = b.f1 AND NOT ( a.f2 = 0 OR a.f - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -462,46 +502,6 @@ explain SELECT a.f3 FROM tb a JOIN tb b ON a.f1 = b.f1 AND NOT ( a.f2 = 0 OR a.f - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/backend/gporca/data/dxl/minidump/Union-On-HJNs.mdp b/src/backend/gporca/data/dxl/minidump/Union-On-HJNs.mdp index 3feb59b90d8b..24241b780632 100644 --- a/src/backend/gporca/data/dxl/minidump/Union-On-HJNs.mdp +++ b/src/backend/gporca/data/dxl/minidump/Union-On-HJNs.mdp @@ -509,7 +509,7 @@ - + @@ -529,7 +529,7 @@ - + @@ -548,7 +548,7 @@ - + @@ -566,7 +566,7 @@ - + @@ -677,7 +677,7 @@ - + @@ -796,7 +796,7 @@ - + @@ -891,7 +891,7 @@ - + @@ -1382,7 +1382,7 @@ - + @@ -1936,7 +1936,7 @@ - + @@ -1956,7 +1956,7 @@ - + @@ -1971,7 +1971,7 @@ - + @@ -2079,7 +2079,7 @@ - + @@ -2195,7 +2195,7 @@ - + @@ -2287,7 +2287,7 @@ - + @@ -2766,7 +2766,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UnionAll.mdp b/src/backend/gporca/data/dxl/minidump/UnionAll.mdp index d26397d44094..cd4e29002c88 100644 --- a/src/backend/gporca/data/dxl/minidump/UnionAll.mdp +++ b/src/backend/gporca/data/dxl/minidump/UnionAll.mdp @@ -8893,7 +8893,7 @@ - + @@ -8907,7 +8907,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UnionAllWithTruncatedOutput.mdp b/src/backend/gporca/data/dxl/minidump/UnionAllWithTruncatedOutput.mdp index fd03037061ab..33a2b0c8f11c 100644 --- a/src/backend/gporca/data/dxl/minidump/UnionAllWithTruncatedOutput.mdp +++ b/src/backend/gporca/data/dxl/minidump/UnionAllWithTruncatedOutput.mdp @@ -324,7 +324,7 @@ - + @@ -338,7 +338,7 @@ - + @@ -445,7 +445,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UnionWithOuterRefs.mdp b/src/backend/gporca/data/dxl/minidump/UnionWithOuterRefs.mdp index 5438cacc0e89..290e6cfd12de 100644 --- a/src/backend/gporca/data/dxl/minidump/UnionWithOuterRefs.mdp +++ b/src/backend/gporca/data/dxl/minidump/UnionWithOuterRefs.mdp @@ -732,7 +732,7 @@ - + @@ -746,7 +746,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UnnestSQJoins.mdp b/src/backend/gporca/data/dxl/minidump/UnnestSQJoins.mdp index 351f735db8b5..59fab2b2328d 100644 --- a/src/backend/gporca/data/dxl/minidump/UnnestSQJoins.mdp +++ b/src/backend/gporca/data/dxl/minidump/UnnestSQJoins.mdp @@ -1283,148 +1283,91 @@ WHERE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + - - - - - + + - - + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -1436,99 +1379,156 @@ WHERE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/UnsupportedStatsPredicate.mdp b/src/backend/gporca/data/dxl/minidump/UnsupportedStatsPredicate.mdp index e8545825f354..83a25ab00768 100644 --- a/src/backend/gporca/data/dxl/minidump/UnsupportedStatsPredicate.mdp +++ b/src/backend/gporca/data/dxl/minidump/UnsupportedStatsPredicate.mdp @@ -1019,7 +1019,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateCardinalityAssert.mdp b/src/backend/gporca/data/dxl/minidump/UpdateCardinalityAssert.mdp index cb3219098caf..7cf544591e2f 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateCardinalityAssert.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateCardinalityAssert.mdp @@ -277,7 +277,7 @@ - + @@ -303,7 +303,7 @@ - + @@ -331,7 +331,7 @@ - + @@ -352,7 +352,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateDistKeyWithNestedJoin.mdp b/src/backend/gporca/data/dxl/minidump/UpdateDistKeyWithNestedJoin.mdp index 026db05d722c..ec10764b0c1c 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateDistKeyWithNestedJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateDistKeyWithNestedJoin.mdp @@ -682,7 +682,7 @@ WHERE t1.id1 = t2.id1; - + @@ -708,7 +708,7 @@ WHERE t1.id1 = t2.id1; - + @@ -736,7 +736,7 @@ WHERE t1.id1 = t2.id1; - + @@ -757,7 +757,7 @@ WHERE t1.id1 = t2.id1; - + @@ -819,7 +819,7 @@ WHERE t1.id1 = t2.id1; - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateDistrKey.mdp b/src/backend/gporca/data/dxl/minidump/UpdateDistrKey.mdp index f6b2e0d6a62d..29e2934bb0b9 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateDistrKey.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateDistrKey.mdp @@ -272,7 +272,7 @@ - + @@ -298,7 +298,7 @@ - + @@ -326,7 +326,7 @@ - + @@ -347,7 +347,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateNotNullCols.mdp b/src/backend/gporca/data/dxl/minidump/UpdateNotNullCols.mdp index 67c9f0767bed..7ea50dcfbd0a 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateNotNullCols.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateNotNullCols.mdp @@ -330,7 +330,7 @@ - + @@ -356,7 +356,7 @@ - + @@ -384,7 +384,7 @@ - + @@ -414,7 +414,7 @@ - + @@ -438,7 +438,7 @@ - + @@ -464,7 +464,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint-2.mdp b/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint-2.mdp index cdf0f5eba8e0..90961adb3ce4 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint-2.mdp @@ -767,7 +767,7 @@ - + @@ -793,7 +793,7 @@ - + @@ -823,7 +823,7 @@ - + @@ -844,7 +844,7 @@ - + @@ -870,7 +870,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateWindowGatherMerge.mdp b/src/backend/gporca/data/dxl/minidump/UpdateWindowGatherMerge.mdp index 5d9fd884460d..07c38bc704fe 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateWindowGatherMerge.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateWindowGatherMerge.mdp @@ -711,7 +711,7 @@ - + @@ -737,7 +737,7 @@ - + @@ -760,7 +760,7 @@ - + @@ -781,7 +781,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateWithHashJoin.mdp b/src/backend/gporca/data/dxl/minidump/UpdateWithHashJoin.mdp index 281d1beda1c1..417eb593cfef 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateWithHashJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateWithHashJoin.mdp @@ -303,7 +303,7 @@ - + @@ -329,7 +329,7 @@ - + @@ -350,7 +350,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UseDistributionSatisfactionForUniversalInnerChild.mdp b/src/backend/gporca/data/dxl/minidump/UseDistributionSatisfactionForUniversalInnerChild.mdp index d9c86bfe6277..e8512f5ddd3f 100644 --- a/src/backend/gporca/data/dxl/minidump/UseDistributionSatisfactionForUniversalInnerChild.mdp +++ b/src/backend/gporca/data/dxl/minidump/UseDistributionSatisfactionForUniversalInnerChild.mdp @@ -107,7 +107,7 @@ - + @@ -120,7 +120,7 @@ - + @@ -135,7 +135,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/cte-duplicate-columns-2.mdp b/src/backend/gporca/data/dxl/minidump/cte-duplicate-columns-2.mdp index 4e173637adde..09eb7d08a398 100644 --- a/src/backend/gporca/data/dxl/minidump/cte-duplicate-columns-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/cte-duplicate-columns-2.mdp @@ -248,7 +248,7 @@ where x1.b = x2.b; - + @@ -403,7 +403,7 @@ where x1.b = x2.b; - + diff --git a/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-AllLevels.mdp b/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-AllLevels.mdp index 1f9bb4d217de..eaae220b32fd 100644 --- a/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-AllLevels.mdp +++ b/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-AllLevels.mdp @@ -340,7 +340,7 @@ select * from pt, t where pt.j = t.t1 and pt.k = t.t2; - + @@ -363,7 +363,7 @@ select * from pt, t where pt.j = t.t1 and pt.k = t.t2; - + diff --git a/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-Level1.mdp b/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-Level1.mdp index 1691735a66d3..ab1a5f68ac25 100644 --- a/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-Level1.mdp +++ b/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-Level1.mdp @@ -358,7 +358,7 @@ select * from pt, t where pt.k = t.t1; - + @@ -381,7 +381,7 @@ select * from pt, t where pt.k = t.t1; - + diff --git a/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-Level2.mdp b/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-Level2.mdp index 177980adfcdc..513297baebbf 100644 --- a/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-Level2.mdp +++ b/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-JoinPred-Level2.mdp @@ -358,7 +358,7 @@ select * from pt, t where pt.j = t.t1; - + @@ -381,7 +381,7 @@ select * from pt, t where pt.j = t.t1; - + diff --git a/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-Nary-Join.mdp b/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-Nary-Join.mdp index 9327f1f8c1de..e6623ed9b908 100644 --- a/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-Nary-Join.mdp +++ b/src/backend/gporca/data/dxl/multilevel-partitioning/Multilevel-Nary-Join.mdp @@ -1429,7 +1429,7 @@ select * from pt, foo, bar where pt.k=foo.a and pt.j = bar.a; - + @@ -1458,7 +1458,7 @@ select * from pt, foo, bar where pt.k=foo.a and pt.j = bar.a; - + @@ -1493,7 +1493,7 @@ select * from pt, foo, bar where pt.k=foo.a and pt.j = bar.a; - + diff --git a/src/backend/gporca/libgpdbcost/src/CCostModelParamsGPDB.cpp b/src/backend/gporca/libgpdbcost/src/CCostModelParamsGPDB.cpp index 673f8f87af95..32ddac576cdf 100644 --- a/src/backend/gporca/libgpdbcost/src/CCostModelParamsGPDB.cpp +++ b/src/backend/gporca/libgpdbcost/src/CCostModelParamsGPDB.cpp @@ -95,7 +95,7 @@ const CDouble CCostModelParamsGPDB::DHJSpillingMemThresholdVal = const CDouble CCostModelParamsGPDB::DHJHashTableInitCostFactorVal = 500.0; // building hash table cost per tuple per column -const CDouble CCostModelParamsGPDB::DHJHashTableColumnCostUnitVal = 5.0e-05; +const CDouble CCostModelParamsGPDB::DHJHashTableColumnCostUnitVal = 1.0e-04; // the unit cost to process each tuple with unit width when building a hash table const CDouble CCostModelParamsGPDB::DHJHashTableWidthCostUnitVal = 3.0e-06; @@ -105,7 +105,7 @@ const CDouble CCostModelParamsGPDB::DHJHashingTupWidthCostUnitVal = 1.97e-05; // feeding cost per tuple per column in hash join if spilling const CDouble CCostModelParamsGPDB::DHJFeedingTupColumnSpillingCostUnitVal = - 1.97e-04; + 8.69e-05; // feeding cost per tuple with unit width in hash join if spilling const CDouble CCostModelParamsGPDB::DHJFeedingTupWidthSpillingCostUnitVal = diff --git a/src/test/regress/expected/bfv_joins.out b/src/test/regress/expected/bfv_joins.out index 99d99a093e70..4919518bf574 100644 --- a/src/test/regress/expected/bfv_joins.out +++ b/src/test/regress/expected/bfv_joins.out @@ -4421,6 +4421,26 @@ select from (values ('')) tmp(b) join lateral (1 row) drop table t1; +-- test that empty table gets hashed instead of a bigger table +create table big_table(s smallint) distributed replicated; +create table empty_table(i int) distributed replicated; +insert into big_table select generate_series(1, 10000); +analyze big_table; +analyze empty_table; +explain select * from big_table, empty_table where s = i; + QUERY PLAN +------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (cost=1.01..150.65 rows=14 width=6) + -> Hash Join (cost=1.01..150.65 rows=14 width=6) + Hash Cond: (big_table.s = empty_table.i) + -> Seq Scan on big_table (cost=0.00..112.00 rows=10000 width=2) + -> Hash (cost=1.00..1.00 rows=1 width=4) + -> Seq Scan on empty_table (cost=0.00..1.00 rows=1 width=4) + Optimizer: Postgres query optimizer +(7 rows) + +drop table big_table; +drop table empty_table; -- Clean up. None of the objects we create are very interesting to keep around. reset search_path; set client_min_messages='warning'; diff --git a/src/test/regress/expected/bfv_joins_optimizer.out b/src/test/regress/expected/bfv_joins_optimizer.out index cec06a68916d..505cd9b14366 100644 --- a/src/test/regress/expected/bfv_joins_optimizer.out +++ b/src/test/regress/expected/bfv_joins_optimizer.out @@ -3368,30 +3368,30 @@ INNER JOIN member_subgroup ON member_group.group_id = member_subgroup.group_id LEFT OUTER JOIN region ON (member_group.group_id IN (12,13,14,15) AND member_subgroup.subgroup_name = region.county_name); - QUERY PLAN ---------------------------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice5; segments: 3) - -> Hash Left Join - Hash Cond: (member_subgroup.subgroup_name = (region.county_name)::text) + -> Hash Right Join + Hash Cond: ((region.county_name)::text = member_subgroup.subgroup_name) Join Filter: (member_group."group_id" = ANY ('{12,13,14,15}'::integer[])) - -> Redistribute Motion 3:3 (slice3; segments: 3) - Hash Key: member_subgroup.subgroup_name - -> Hash Join - Hash Cond: (member_group."group_id" = member."group_id") - -> Seq Scan on member_group - -> Hash - -> Redistribute Motion 3:3 (slice2; segments: 3) - Hash Key: member_subgroup."group_id" - -> Hash Join - Hash Cond: (member."group_id" = member_subgroup."group_id") - -> Seq Scan on member - -> Hash - -> Broadcast Motion 3:3 (slice1; segments: 3) - -> Seq Scan on member_subgroup + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: region.county_name + -> Seq Scan on region -> Hash -> Redistribute Motion 3:3 (slice4; segments: 3) - Hash Key: region.county_name - -> Seq Scan on region + Hash Key: member_subgroup.subgroup_name + -> Hash Join + Hash Cond: (member_group."group_id" = member."group_id") + -> Seq Scan on member_group + -> Hash + -> Redistribute Motion 3:3 (slice3; segments: 3) + Hash Key: member_subgroup."group_id" + -> Hash Join + Hash Cond: (member."group_id" = member_subgroup."group_id") + -> Seq Scan on member + -> Hash + -> Broadcast Motion 3:3 (slice2; segments: 3) + -> Seq Scan on member_subgroup Optimizer: Pivotal Optimizer (GPORCA) (23 rows) @@ -4412,6 +4412,26 @@ select from (values ('')) tmp(b) join lateral (1 row) drop table t1; +-- test that empty table gets hashed instead of a bigger table +create table big_table(s smallint) distributed replicated; +create table empty_table(i int) distributed replicated; +insert into big_table select generate_series(1, 10000); +analyze big_table; +analyze empty_table; +explain select * from big_table, empty_table where s = i; + QUERY PLAN +------------------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) (cost=0.00..863.96 rows=1 width=6) + -> Hash Join (cost=0.00..863.96 rows=1 width=6) + Hash Cond: ((big_table.s)::integer = empty_table.i) + -> Seq Scan on big_table (cost=0.00..431.18 rows=10000 width=2) + -> Hash (cost=431.00..431.00 rows=1 width=4) + -> Seq Scan on empty_table (cost=0.00..431.00 rows=1 width=4) + Optimizer: Pivotal Optimizer (GPORCA) +(7 rows) + +drop table big_table; +drop table empty_table; -- Clean up. None of the objects we create are very interesting to keep around. reset search_path; set client_min_messages='warning'; diff --git a/src/test/regress/expected/bfv_subquery_optimizer.out b/src/test/regress/expected/bfv_subquery_optimizer.out index 0430d10952e4..81f6e88611bb 100644 --- a/src/test/regress/expected/bfv_subquery_optimizer.out +++ b/src/test/regress/expected/bfv_subquery_optimizer.out @@ -507,20 +507,22 @@ analyze t1; -- Function Scan explain select count(*) from pg_backend_pid() b(a) where b.a % 100000 in (select a from t1); - QUERY PLAN ------------------------------------------------------------------------------------- - Aggregate (cost=0.00..450.95 rows=1 width=8) - -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..450.95 rows=1 width=1) - -> Hash Semi Join (cost=0.00..450.95 rows=1 width=1) - Hash Cond: ((b % 100000) = a) - -> Result (cost=0.00..0.00 rows=1 width=4) - -> Result (cost=0.00..0.00 rows=1 width=4) - -> Result (cost=0.00..0.00 rows=1 width=4) + QUERY PLAN +----------------------------------------------------------------------------------------------------------- + Aggregate (cost=0.00..450.99 rows=1 width=8) + -> GroupAggregate (cost=0.00..450.99 rows=1 width=1) + Group Key: + -> Sort (cost=0.00..450.99 rows=1 width=1) + Sort Key: + -> Hash Join (cost=0.00..450.99 rows=1 width=1) + Hash Cond: (a = (b % 100000)) + -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..433.36 rows=100000 width=4) + -> Seq Scan on t1 (cost=0.00..431.62 rows=33334 width=4) + -> Hash (cost=0.00..0.00 rows=1 width=5) + -> Result (cost=0.00..0.00 rows=1 width=5) -> Result (cost=0.00..0.00 rows=1 width=1) - -> Hash (cost=431.62..431.62 rows=33334 width=4) - -> Seq Scan on t1 (cost=0.00..431.62 rows=33334 width=4) Optimizer: Pivotal Optimizer (GPORCA) -(11 rows) +(13 rows) select count(*) from pg_backend_pid() b(a) where b.a % 100000 in (select a from t1); count @@ -537,10 +539,10 @@ explain select count(*) from ( values :lots_of_values ) as b(a) where b.a % 100000 in (select a from t1); QUERY PLAN --------------------------------------------------------------------------------------------------- - Aggregate (cost=0.00..443.14 rows=1 width=8) - -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..443.14 rows=1 width=8) - -> Aggregate (cost=0.00..443.14 rows=1 width=8) - -> Hash Semi Join (cost=0.00..443.14 rows=22000 width=1) + Aggregate (cost=0.00..446.47 rows=1 width=8) + -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..446.47 rows=1 width=8) + -> Aggregate (cost=0.00..446.47 rows=1 width=8) + -> Hash Semi Join (cost=0.00..446.47 rows=22000 width=1) Hash Cond: (("Values".column1 % 100000) = t1.a) -> Result (cost=0.00..0.95 rows=22000 width=4) -> Result (cost=0.00..0.95 rows=22000 width=4) diff --git a/src/test/regress/expected/join_optimizer.out b/src/test/regress/expected/join_optimizer.out index 26089b4e78c5..4846cec6f8fd 100755 --- a/src/test/regress/expected/join_optimizer.out +++ b/src/test/regress/expected/join_optimizer.out @@ -3409,32 +3409,32 @@ select count(*) from tenk1 a join tenk1 b on a.unique1 = b.unique2 left join tenk1 c on a.unique2 = b.unique1 and c.thousand = a.thousand join int4_tbl on b.thousand = f1; - QUERY PLAN ------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------ Aggregate -> Gather Motion 3:1 (slice5; segments: 3) -> Aggregate - -> Hash Left Join - Hash Cond: (tenk1.thousand = tenk1_2.thousand) - Join Filter: (tenk1.unique2 = tenk1_1.unique1) - -> Redistribute Motion 3:3 (slice3; segments: 3) + -> Hash Right Join + Hash Cond: (tenk1.thousand = tenk1_1.thousand) + Join Filter: (tenk1_1.unique2 = tenk1_2.unique1) + -> Redistribute Motion 3:3 (slice1; segments: 3) Hash Key: tenk1.thousand - -> Hash Join - Hash Cond: (tenk1_1.thousand = int4_tbl.f1) - -> Hash Join - Hash Cond: (tenk1.unique1 = tenk1_1.unique2) - -> Seq Scan on tenk1 - -> Hash - -> Redistribute Motion 3:3 (slice1; segments: 3) - Hash Key: tenk1_1.unique2 - -> Seq Scan on tenk1 tenk1_1 - -> Hash - -> Broadcast Motion 3:3 (slice2; segments: 3) - -> Seq Scan on int4_tbl + -> Seq Scan on tenk1 -> Hash -> Redistribute Motion 3:3 (slice4; segments: 3) - Hash Key: tenk1_2.thousand - -> Seq Scan on tenk1 tenk1_2 + Hash Key: tenk1_1.thousand + -> Hash Join + Hash Cond: (tenk1_2.thousand = int4_tbl.f1) + -> Hash Join + Hash Cond: (tenk1_1.unique1 = tenk1_2.unique2) + -> Seq Scan on tenk1 tenk1_1 + -> Hash + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: tenk1_2.unique2 + -> Seq Scan on tenk1 tenk1_2 + -> Hash + -> Broadcast Motion 3:3 (slice3; segments: 3) + -> Seq Scan on int4_tbl Optimizer: Pivotal Optimizer (GPORCA) (25 rows) @@ -3454,37 +3454,37 @@ select b.unique1 from join int4_tbl i1 on b.thousand = f1 right join int4_tbl i2 on i2.f1 = b.tenthous order by 1; - QUERY PLAN ------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------ Gather Motion 3:1 (slice6; segments: 3) - Merge Key: tenk1.unique1 + Merge Key: tenk1_1.unique1 -> Sort - Sort Key: tenk1.unique1 + Sort Key: tenk1_1.unique1 -> Hash Right Join - Hash Cond: (tenk1.tenthous = int4_tbl_1.f1) + Hash Cond: (tenk1_1.tenthous = int4_tbl_1.f1) -> Redistribute Motion 3:3 (slice5; segments: 3) - Hash Key: tenk1.tenthous - -> Hash Left Join - Hash Cond: (tenk1_1.thousand = tenk1_2.thousand) - Join Filter: (tenk1.unique1 = 42) - -> Redistribute Motion 3:3 (slice3; segments: 3) - Hash Key: tenk1_1.thousand - -> Hash Join - Hash Cond: (tenk1.thousand = int4_tbl.f1) - -> Hash Join - Hash Cond: (tenk1.unique2 = tenk1_1.unique1) - -> Redistribute Motion 3:3 (slice1; segments: 3) - Hash Key: tenk1.unique2 - -> Seq Scan on tenk1 - -> Hash - -> Seq Scan on tenk1 tenk1_1 - -> Hash - -> Broadcast Motion 3:3 (slice2; segments: 3) - -> Seq Scan on int4_tbl + Hash Key: tenk1_1.tenthous + -> Hash Right Join + Hash Cond: (tenk1.thousand = tenk1_2.thousand) + Join Filter: (tenk1_1.unique1 = 42) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: tenk1.thousand + -> Seq Scan on tenk1 -> Hash -> Redistribute Motion 3:3 (slice4; segments: 3) Hash Key: tenk1_2.thousand - -> Seq Scan on tenk1 tenk1_2 + -> Hash Join + Hash Cond: (tenk1_1.thousand = int4_tbl.f1) + -> Hash Join + Hash Cond: (tenk1_1.unique2 = tenk1_2.unique1) + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: tenk1_1.unique2 + -> Seq Scan on tenk1 tenk1_1 + -> Hash + -> Seq Scan on tenk1 tenk1_2 + -> Hash + -> Broadcast Motion 3:3 (slice3; segments: 3) + -> Seq Scan on int4_tbl -> Hash -> Seq Scan on int4_tbl int4_tbl_1 Optimizer: Pivotal Optimizer (GPORCA) @@ -4372,25 +4372,25 @@ explain (costs off) SELECT b.* FROM b LEFT JOIN c ON b.c_id = c.id; explain (costs off) SELECT a.* FROM a LEFT JOIN (b left join c on b.c_id = c.id) ON (a.b_id = b.id); - QUERY PLAN ------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------ Gather Motion 3:1 (slice4; segments: 3) - -> Hash Left Join - Hash Cond: (a.b_id = b.id) - -> Redistribute Motion 3:3 (slice1; segments: 3) - Hash Key: a.b_id - -> Seq Scan on a + -> Hash Right Join + Hash Cond: (b.id = a.b_id) + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: b.id + -> Nested Loop Left Join + Join Filter: true + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: b.c_id + -> Seq Scan on b + -> Index Scan using c_pkey on c + Index Cond: (id = b.c_id) -> Hash -> Redistribute Motion 3:3 (slice3; segments: 3) - Hash Key: b.id - -> Nested Loop Left Join - Join Filter: true - -> Redistribute Motion 3:3 (slice2; segments: 3) - Hash Key: b.c_id - -> Seq Scan on b - -> Index Scan using c_pkey on c - Index Cond: (id = b.c_id) - Optimizer: Pivotal Optimizer (GPORCA) version 2.64.0 + Hash Key: a.b_id + -> Seq Scan on a + Optimizer: Pivotal Optimizer (GPORCA) (17 rows) -- check optimization of outer join within another special join diff --git a/src/test/regress/expected/notin_optimizer.out b/src/test/regress/expected/notin_optimizer.out index 572675c90836..c5d4a2b91c27 100644 --- a/src/test/regress/expected/notin_optimizer.out +++ b/src/test/regress/expected/notin_optimizer.out @@ -232,12 +232,12 @@ explain select c1 from t1, Hash Cond: (t2.c2 = t3.c3) -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..862.00 rows=1 width=8) -> Hash Join (cost=0.00..862.00 rows=1 width=8) - Hash Cond: (t2.c2 = t1.c1) - -> Seq Scan on t2 (cost=0.00..431.00 rows=1 width=4) - Filter: (c2 > 4) - -> Hash (cost=431.00..431.00 rows=2 width=4) - -> Seq Scan on t1 (cost=0.00..431.00 rows=2 width=4) - Filter: (c1 > 4) + Hash Cond: (t1.c1 = t2.c2) + -> Seq Scan on t1 (cost=0.00..431.00 rows=2 width=4) + Filter: (c1 > 4) + -> Hash (cost=431.00..431.00 rows=1 width=4) + -> Seq Scan on t2 (cost=0.00..431.00 rows=1 width=4) + Filter: (c2 > 4) -> Hash (cost=431.00..431.00 rows=1 width=4) -> Gather Motion 3:1 (slice2; segments: 3) (cost=0.00..431.00 rows=3 width=4) -> Seq Scan on t3 (cost=0.00..431.00 rows=1 width=4) diff --git a/src/test/regress/expected/qp_correlated_query_optimizer.out b/src/test/regress/expected/qp_correlated_query_optimizer.out index 201eae3b1e6c..3db92119c070 100644 --- a/src/test/regress/expected/qp_correlated_query_optimizer.out +++ b/src/test/regress/expected/qp_correlated_query_optimizer.out @@ -509,23 +509,28 @@ select A.j from A, B, C where A.j = (select C.j from C where C.j = A.j and C.i n explain select A.i from A where A.j = (select C.j from C where C.j = A.j and C.i = any (select B.i from B where C.i = B.i and B.i !=10)); QUERY PLAN ----------------------------------------------------------------------------------------------------------------- - Gather Motion 3:1 (slice2; segments: 3) (cost=0.00..1765379.74 rows=5 width=4) - -> Result (cost=0.00..1765379.74 rows=2 width=4) - -> Seq Scan on a (cost=0.00..1765379.74 rows=2 width=4) + Gather Motion 3:1 (slice2; segments: 3) (cost=0.00..1765380.20 rows=5 width=4) + -> Result (cost=0.00..1765380.20 rows=2 width=4) + -> Seq Scan on a (cost=0.00..1765380.20 rows=2 width=4) Filter: (j = (SubPlan 1)) SubPlan 1 (slice2; segments: 3) -> Result (cost=0.00..862.00 rows=1 width=4) Filter: (c.j = a.j) -> Materialize (cost=0.00..862.00 rows=6 width=4) -> Broadcast Motion 3:3 (slice1; segments: 3) (cost=0.00..862.00 rows=6 width=4) - -> Hash Semi Join (cost=0.00..862.00 rows=2 width=4) + -> Hash Join (cost=0.00..862.00 rows=2 width=4) Hash Cond: ((c.i = b.i) AND (c.i = b.i)) -> Seq Scan on c (cost=0.00..431.00 rows=3 width=8) + Filter: ((i < 10) OR (i > 10)) -> Hash (cost=431.00..431.00 rows=2 width=4) - -> Seq Scan on b (cost=0.00..431.00 rows=2 width=4) - Filter: (i <> 10) - Optimizer: PQO version 3.27.0 -(16 rows) + -> GroupAggregate (cost=0.00..431.00 rows=2 width=4) + Group Key: b.i + -> Sort (cost=0.00..431.00 rows=2 width=4) + Sort Key: b.i + -> Seq Scan on b (cost=0.00..431.00 rows=2 width=4) + Filter: ((i <> 10) AND ((i < 10) OR (i > 10))) + Optimizer: Pivotal Optimizer (GPORCA) +(21 rows) select A.i from A where A.j = (select C.j from C where C.j = A.j and C.i = any (select B.i from B where C.i = B.i and B.i !=10)); i @@ -627,39 +632,44 @@ select * from A,B where A.j = any (select C.j from C where C.j = A.j and B.i = a (4 rows) explain select A.i, B.i, C.j from A, B, C where A.j = (select C.j from C where C.j = A.j and C.i = any (select B.i from B where C.i = B.i and B.i !=10)) order by A.i, B.i, C.j limit 10; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------ - Limit (cost=0.00..1852039752658.48 rows=4 width=12) - -> Gather Motion 3:1 (slice4; segments: 3) (cost=0.00..1852039752658.48 rows=10 width=12) + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit (cost=0.00..1852040061841.32 rows=4 width=12) + -> Gather Motion 3:1 (slice4; segments: 3) (cost=0.00..1852040061841.32 rows=10 width=12) Merge Key: a.i, b.i, c.j - -> Limit (cost=0.00..1852039752658.48 rows=4 width=12) - -> Sort (cost=0.00..1852039752658.48 rows=90 width=12) + -> Limit (cost=0.00..1852040061841.32 rows=4 width=12) + -> Sort (cost=0.00..1852040061841.32 rows=90 width=12) Sort Key: a.i, b.i, c.j - -> Nested Loop (cost=0.00..1852039752658.44 rows=90 width=12) + -> Nested Loop (cost=0.00..1852040061841.28 rows=90 width=12) Join Filter: true - -> Nested Loop (cost=0.00..1808631708.95 rows=10 width=8) + -> Nested Loop (cost=0.00..1808632010.89 rows=10 width=8) Join Filter: true - -> Broadcast Motion 3:3 (slice3; segments: 3) (cost=0.00..1765379.90 rows=5 width=4) - -> Result (cost=0.00..1765379.90 rows=2 width=4) - -> Seq Scan on a (cost=0.00..1765379.90 rows=2 width=4) + -> Broadcast Motion 3:3 (slice3; segments: 3) (cost=0.00..1765380.20 rows=5 width=4) + -> Result (cost=0.00..1765380.20 rows=2 width=4) + -> Seq Scan on a (cost=0.00..1765380.20 rows=2 width=4) Filter: (j = (SubPlan 1)) SubPlan 1 (slice3; segments: 3) -> Result (cost=0.00..862.00 rows=1 width=4) Filter: (c_1.j = a.j) -> Materialize (cost=0.00..862.00 rows=6 width=4) -> Broadcast Motion 3:3 (slice2; segments: 3) (cost=0.00..862.00 rows=6 width=4) - -> Hash Semi Join (cost=0.00..862.00 rows=2 width=4) + -> Hash Join (cost=0.00..862.00 rows=2 width=4) Hash Cond: ((c_1.i = b_1.i) AND (c_1.i = b_1.i)) -> Seq Scan on c c_1 (cost=0.00..431.00 rows=3 width=8) + Filter: ((i < 10) OR (i > 10)) -> Hash (cost=431.00..431.00 rows=2 width=4) - -> Seq Scan on b b_1 (cost=0.00..431.00 rows=2 width=4) - Filter: (i <> 10) + -> GroupAggregate (cost=0.00..431.00 rows=2 width=4) + Group Key: b_1.i + -> Sort (cost=0.00..431.00 rows=2 width=4) + Sort Key: b_1.i + -> Seq Scan on b b_1 (cost=0.00..431.00 rows=2 width=4) + Filter: ((i <> 10) AND ((i < 10) OR (i > 10))) -> Seq Scan on b (cost=0.00..431.00 rows=2 width=4) -> Materialize (cost=0.00..431.00 rows=9 width=4) -> Broadcast Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=9 width=4) -> Seq Scan on c (cost=0.00..431.00 rows=3 width=4) Optimizer: Pivotal Optimizer (GPORCA) -(30 rows) +(35 rows) select A.i, B.i, C.j from A, B, C where A.j = (select C.j from C where C.j = A.j and C.i = any (select B.i from B where C.i = B.i and B.i !=10)) order by A.i, B.i, C.j limit 10; i | i | j @@ -731,36 +741,36 @@ select A.i, B.i, C.j from A, B, C where A.j = any ( select C.j from C where not (10 rows) explain select A.i, B.i, C.j from A, B, C where A.j = any (select C.j from C where C.j = A.j and not exists (select sum(B.i) from B where C.i = B.i and C.i !=10)) order by A.i, B.i, C.j limit 10; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------- - Limit (cost=0.00..1356692465.22 rows=4 width=12) - -> Gather Motion 3:1 (slice4; segments: 3) (cost=0.00..1356692465.22 rows=10 width=12) + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------- + Limit (cost=0.00..1356692610.57 rows=4 width=12) + -> Gather Motion 3:1 (slice4; segments: 3) (cost=0.00..1356692610.57 rows=10 width=12) Merge Key: a.i, b.i, c.j - -> Limit (cost=0.00..1356692465.22 rows=4 width=12) - -> Sort (cost=0.00..1356692465.22 rows=18 width=12) + -> Limit (cost=0.00..1356692610.57 rows=4 width=12) + -> Sort (cost=0.00..1356692610.57 rows=18 width=12) Sort Key: a.i, b.i, c.j - -> Nested Loop (cost=0.00..1356692465.22 rows=18 width=12) - Join Filter: true - -> Nested Loop (cost=0.00..1324032.98 rows=2 width=8) - Join Filter: true - -> Broadcast Motion 3:3 (slice3; segments: 3) (cost=0.00..431.00 rows=1 width=4) - -> Hash Join (cost=0.00..431.00 rows=1 width=4) - Hash Cond: "outer".j = a.j - -> Result (cost=0.00..0.00 rows=0 width=4) - -> HashAggregate (cost=0.00..0.00 rows=0 width=4) - Group By: NULL::integer - -> Result (cost=0.00..0.00 rows=0 width=4) - One-Time Filter: false - -> Hash (cost=431.00..431.00 rows=2 width=8) - -> Redistribute Motion 3:3 (slice2; segments: 3) (cost=0.00..431.00 rows=2 width=8) - Hash Key: a.j - -> Seq Scan on a (cost=0.00..431.00 rows=2 width=8) - -> Seq Scan on b (cost=0.00..431.00 rows=2 width=4) - -> Materialize (cost=0.00..431.00 rows=9 width=4) - -> Broadcast Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=9 width=4) - -> Seq Scan on c (cost=0.00..431.00 rows=3 width=4) - Settings: optimizer=on - Optimizer status: Pivotal Optimizer (GPORCA) version 2.34.0 + -> Hash Join (cost=0.00..1356692610.56 rows=18 width=12) + Hash Cond: ("outer".j = a.j) + -> Result (cost=0.00..0.00 rows=0 width=4) + -> HashAggregate (cost=0.00..0.00 rows=0 width=4) + Group Key: NULL::integer + -> Result (cost=0.00..0.00 rows=0 width=4) + One-Time Filter: false + -> Hash (cost=1356692610.51..1356692610.51 rows=90 width=16) + -> Redistribute Motion 3:3 (slice3; segments: 3) (cost=0.00..1356692610.51 rows=90 width=16) + Hash Key: a.j + -> Nested Loop (cost=0.00..1356692610.50 rows=90 width=16) + Join Filter: true + -> Nested Loop (cost=0.00..1324033.12 rows=10 width=12) + Join Filter: true + -> Seq Scan on a (cost=0.00..431.00 rows=2 width=8) + -> Materialize (cost=0.00..431.00 rows=6 width=4) + -> Broadcast Motion 3:3 (slice2; segments: 3) (cost=0.00..431.00 rows=6 width=4) + -> Seq Scan on b (cost=0.00..431.00 rows=2 width=4) + -> Materialize (cost=0.00..431.00 rows=9 width=4) + -> Broadcast Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=9 width=4) + -> Seq Scan on c (cost=0.00..431.00 rows=3 width=4) + Optimizer: Pivotal Optimizer (GPORCA) (28 rows) select A.i, B.i, C.j from A, B, C where A.j = any (select C.j from C where C.j = A.j and not exists (select sum(B.i) from B where C.i = B.i and C.i !=10)) order by A.i, B.i, C.j limit 10; @@ -1494,35 +1504,35 @@ select * from B where not exists (select * from C,A where C.i in (select C.i fro (4 rows) explain select * from A where A.i in (select C.j from C,B where B.i in (select i from C)); - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice4; segments: 3) (cost=0.00..1324895.30 rows=5 width=8) -> Hash Join (cost=0.00..1324895.30 rows=2 width=8) - Hash Cond: a.i = c.j - -> Seq Scan on a (cost=0.00..431.00 rows=2 width=8) - -> Hash (cost=1324464.30..1324464.30 rows=3 width=4) - -> GroupAggregate (cost=0.00..1324464.30 rows=3 width=4) - Group Key: c.j - -> Sort (cost=0.00..1324464.30 rows=3 width=4) - Sort Key: c.j - -> Redistribute Motion 3:3 (slice3; segments: 3) (cost=0.00..1324464.30 rows=3 width=4) - Hash Key: c.j - -> GroupAggregate (cost=0.00..1324464.30 rows=3 width=4) - Group Key: c.j - -> Sort (cost=0.00..1324464.30 rows=18 width=4) - Sort Key: c.j - -> Hash Semi Join (cost=0.00..1324464.30 rows=18 width=4) - Hash Cond: b.i = c_1.i - -> Redistribute Motion 3:3 (slice2; segments: 3) (cost=0.00..1324033.29 rows=18 width=8) - Hash Key: b.i - -> Nested Loop (cost=0.00..1324033.29 rows=18 width=8) - Join Filter: true - -> Broadcast Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=6 width=4) - -> Seq Scan on b (cost=0.00..431.00 rows=2 width=4) - -> Seq Scan on c (cost=0.00..431.00 rows=3 width=4) - -> Hash (cost=431.00..431.00 rows=3 width=4) - -> Seq Scan on c c_1 (cost=0.00..431.00 rows=3 width=4) - Optimizer: Pivotal Optimizer (GPORCA) version 2.74.0 + Hash Cond: (c.j = a.i) + -> GroupAggregate (cost=0.00..1324464.30 rows=3 width=4) + Group Key: c.j + -> Sort (cost=0.00..1324464.30 rows=3 width=4) + Sort Key: c.j + -> Redistribute Motion 3:3 (slice3; segments: 3) (cost=0.00..1324464.30 rows=3 width=4) + Hash Key: c.j + -> GroupAggregate (cost=0.00..1324464.30 rows=3 width=4) + Group Key: c.j + -> Sort (cost=0.00..1324464.30 rows=18 width=4) + Sort Key: c.j + -> Hash Semi Join (cost=0.00..1324464.30 rows=18 width=4) + Hash Cond: (b.i = c_1.i) + -> Redistribute Motion 3:3 (slice2; segments: 3) (cost=0.00..1324033.29 rows=18 width=8) + Hash Key: b.i + -> Nested Loop (cost=0.00..1324033.29 rows=18 width=8) + Join Filter: true + -> Broadcast Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=6 width=4) + -> Seq Scan on b (cost=0.00..431.00 rows=2 width=4) + -> Seq Scan on c (cost=0.00..431.00 rows=3 width=4) + -> Hash (cost=431.00..431.00 rows=3 width=4) + -> Seq Scan on c c_1 (cost=0.00..431.00 rows=3 width=4) + -> Hash (cost=431.00..431.00 rows=2 width=8) + -> Seq Scan on a (cost=0.00..431.00 rows=2 width=8) + Optimizer: Pivotal Optimizer (GPORCA) (27 rows) select * from A where A.i in (select C.j from C,B where B.i in (select i from C)); diff --git a/src/test/regress/expected/qp_query_execution.out b/src/test/regress/expected/qp_query_execution.out index 3ebf8d5fa8b4..1e21d6d5ede0 100644 --- a/src/test/regress/expected/qp_query_execution.out +++ b/src/test/regress/expected/qp_query_execution.out @@ -444,7 +444,7 @@ insert into b select i%7, i%10, i , i || 'SOME NUMBER', i % 4 from generate_seri insert into b select i%7, i%10, i , i || 'SOME NUMBER', i % 4 from generate_series(1, 1000) i; analyze abbp; analyze b; -select qx_count_operator('select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6;', 'Hash Right Join', 'Hash Left Join'); +select qx_count_operator('select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6;', 'Hash Right Join', 'Hash Right Join'); qx_count_operator ------------------- 1 diff --git a/src/test/regress/expected/rpt_optimizer.out b/src/test/regress/expected/rpt_optimizer.out index 17a5406dc34f..26d324831edf 100644 --- a/src/test/regress/expected/rpt_optimizer.out +++ b/src/test/regress/expected/rpt_optimizer.out @@ -1217,20 +1217,21 @@ select d from rand_tab where d in (select distinct c from rep_tab); -- -- join derives EdtHashed explain select c from rep_tab where c in (select distinct a from dist_tab); - QUERY PLAN ----------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..862.00 rows=2 width=4) - -> Hash Join (cost=0.00..862.00 rows=1 width=4) - Hash Cond: (dist_tab.a = rep_tab.c) - -> GroupAggregate (cost=0.00..431.00 rows=1 width=4) - Group Key: dist_tab.a - -> Sort (cost=0.00..431.00 rows=2 width=4) - Sort Key: dist_tab.a - -> Seq Scan on dist_tab (cost=0.00..431.00 rows=2 width=4) - -> Hash (cost=431.00..431.00 rows=2 width=4) + -> Hash Semi Join (cost=0.00..862.00 rows=1 width=4) + Hash Cond: (rep_tab.c = dist_tab.a) + -> Result (cost=0.00..431.00 rows=1 width=4) -> Seq Scan on rep_tab (cost=0.00..431.00 rows=2 width=4) + -> Hash (cost=431.00..431.00 rows=1 width=4) + -> GroupAggregate (cost=0.00..431.00 rows=1 width=4) + Group Key: dist_tab.a + -> Sort (cost=0.00..431.00 rows=2 width=4) + Sort Key: dist_tab.a + -> Seq Scan on dist_tab (cost=0.00..431.00 rows=2 width=4) Optimizer: Pivotal Optimizer (GPORCA) -(11 rows) +(12 rows) select c from rep_tab where c in (select distinct a from dist_tab); c @@ -1245,22 +1246,19 @@ select c from rep_tab where c in (select distinct a from dist_tab); -- -- join derives EdtHashed explain select c from rep_tab where c in (select distinct d from rand_tab); - QUERY PLAN ------------------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------ Gather Motion 3:1 (slice2; segments: 3) (cost=0.00..862.00 rows=2 width=4) - -> Hash Join (cost=0.00..862.00 rows=1 width=4) - Hash Cond: (rand_tab.d = rep_tab.c) - -> GroupAggregate (cost=0.00..431.00 rows=1 width=4) - Group Key: rand_tab.d - -> Sort (cost=0.00..431.00 rows=1 width=4) - Sort Key: rand_tab.d - -> Redistribute Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=4) - Hash Key: rand_tab.d - -> Seq Scan on rand_tab (cost=0.00..431.00 rows=1 width=4) - -> Hash (cost=431.00..431.00 rows=2 width=4) + -> Hash Semi Join (cost=0.00..862.00 rows=1 width=4) + Hash Cond: (rep_tab.c = rand_tab.d) + -> Result (cost=0.00..431.00 rows=1 width=4) -> Seq Scan on rep_tab (cost=0.00..431.00 rows=2 width=4) + -> Hash (cost=431.00..431.00 rows=1 width=4) + -> Redistribute Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=4) + Hash Key: rand_tab.d + -> Seq Scan on rand_tab (cost=0.00..431.00 rows=1 width=4) Optimizer: Pivotal Optimizer (GPORCA) -(13 rows) +(10 rows) select c from rep_tab where c in (select distinct d from rand_tab); c diff --git a/src/test/regress/expected/subselect_gp_optimizer.out b/src/test/regress/expected/subselect_gp_optimizer.out index f103ecc69161..3f7c04fabec2 100644 --- a/src/test/regress/expected/subselect_gp_optimizer.out +++ b/src/test/regress/expected/subselect_gp_optimizer.out @@ -1480,30 +1480,34 @@ EXPLAIN SELECT '' AS six, f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL EXPLAIN SELECT '' AS six, f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL WHERE f1 IN (SELECT f2 FROM SUBSELECT_TBL WHERE f2 IN (SELECT f1 FROM SUBSELECT_TBL)) ORDER BY 2; - QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------- Result (cost=0.00..1293.00 rows=2 width=12) - -> Gather Motion 3:1 (slice2; segments: 3) (cost=0.00..1293.00 rows=5 width=4) + -> Gather Motion 3:1 (slice2; segments: 3) (cost=0.00..1293.00 rows=4 width=4) Merge Key: subselect_tbl.f1 -> Sort (cost=0.00..1293.00 rows=2 width=4) Sort Key: subselect_tbl.f1 -> Hash Join (cost=0.00..1293.00 rows=2 width=4) - Hash Cond: subselect_tbl.f1 = subselect_tbl_1.f2 + Hash Cond: (subselect_tbl.f1 = subselect_tbl_2.f2) -> Seq Scan on subselect_tbl (cost=0.00..431.00 rows=3 width=4) -> Hash (cost=862.00..862.00 rows=2 width=4) - -> GroupAggregate (cost=0.00..862.00 rows=2 width=4) - Group Key: subselect_tbl_1.f2 - -> Sort (cost=0.00..862.00 rows=3 width=4) - Sort Key: subselect_tbl_1.f2 - -> Hash Semi Join (cost=0.00..862.00 rows=3 width=4) - Hash Cond: subselect_tbl_1.f2 = subselect_tbl_2.f1 - -> Redistribute Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=3 width=4) - Hash Key: subselect_tbl_1.f2 - -> Seq Scan on subselect_tbl subselect_tbl_1 (cost=0.00..431.00 rows=3 width=4) - -> Hash (cost=431.00..431.00 rows=3 width=4) - -> Seq Scan on subselect_tbl subselect_tbl_2 (cost=0.00..431.00 rows=3 width=4) - Optimizer: Pivotal Optimizer (GPORCA) version 2.74.0 -(21 rows) + -> Hash Join (cost=0.00..862.00 rows=2 width=4) + Hash Cond: (subselect_tbl_1.f1 = subselect_tbl_2.f2) + -> GroupAggregate (cost=0.00..431.00 rows=3 width=4) + Group Key: subselect_tbl_1.f1 + -> Sort (cost=0.00..431.00 rows=3 width=4) + Sort Key: subselect_tbl_1.f1 + -> Seq Scan on subselect_tbl subselect_tbl_1 (cost=0.00..431.00 rows=3 width=4) + -> Hash (cost=431.00..431.00 rows=3 width=4) + -> GroupAggregate (cost=0.00..431.00 rows=3 width=4) + Group Key: subselect_tbl_2.f2 + -> Sort (cost=0.00..431.00 rows=3 width=4) + Sort Key: subselect_tbl_2.f2 + -> Redistribute Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=3 width=4) + Hash Key: subselect_tbl_2.f2 + -> Seq Scan on subselect_tbl subselect_tbl_2 (cost=0.00..431.00 rows=3 width=4) + Optimizer: Pivotal Optimizer (GPORCA) +(25 rows) EXPLAIN SELECT '' AS three, f1, f2 FROM SUBSELECT_TBL @@ -3287,20 +3291,23 @@ analyze table_left; analyze table_right; -- two types of semi join tests explain (costs off) select * from table_left where exists (select 1 from table_right where l1 = r1); - QUERY PLAN ------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------ Gather Motion 3:1 (slice2; segments: 3) - -> Hash Semi Join - Hash Cond: (table_left.l1 = table_right.r1) - -> Seq Scan on table_left - Filter: (NOT (l1 IS NULL)) + -> Hash Join + Hash Cond: (table_right.r1 = table_left.l1) + -> HashAggregate + Group Key: table_right.r1 + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: table_right.r1 + -> Result + -> Result + -> Seq Scan on table_right -> Hash - -> Result - -> Redistribute Motion 3:3 (slice1; segments: 3) - Hash Key: table_right.r1 - -> Seq Scan on table_right + -> Seq Scan on table_left + Filter: (NOT (l1 IS NULL)) Optimizer: Pivotal Optimizer (GPORCA) -(11 rows) +(14 rows) select * from table_left where exists (select 1 from table_right where l1 = r1); l1 | l2 @@ -3312,15 +3319,17 @@ explain (costs off) select * from table_left where l1 in (select r1 from table_r QUERY PLAN ------------------------------------------------------------------ Gather Motion 3:1 (slice2; segments: 3) - -> Hash Semi Join - Hash Cond: (table_left.l1 = table_right.r1) - -> Seq Scan on table_left - -> Hash + -> Hash Join + Hash Cond: (table_right.r1 = table_left.l1) + -> HashAggregate + Group Key: table_right.r1 -> Redistribute Motion 3:3 (slice1; segments: 3) Hash Key: table_right.r1 -> Seq Scan on table_right + -> Hash + -> Seq Scan on table_left Optimizer: Pivotal Optimizer (GPORCA) -(9 rows) +(11 rows) select * from table_left where exists (select 1 from table_right where l1 = r1); l1 | l2 diff --git a/src/test/regress/expected/update_gp_optimizer.out b/src/test/regress/expected/update_gp_optimizer.out index 86552f3f2023..a203f7f99b2e 100644 --- a/src/test/regress/expected/update_gp_optimizer.out +++ b/src/test/regress/expected/update_gp_optimizer.out @@ -227,19 +227,19 @@ INSERT INTO t_strewn SELECT generate_series(1, 16); INSERT INTO t_strewn2 SELECT generate_series(2, 17); EXPLAIN (costs off) UPDATE t1 SET j = t_strewn.i FROM t_strewn WHERE t_strewn.i = t1.i; - QUERY PLAN ------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------ Update -> Redistribute Motion 3:3 (slice2; segments: 3) Hash Key: t1.i -> Split -> Hash Join - Hash Cond: (t1.i = t_strewn.i) - -> Seq Scan on t1 + Hash Cond: (t_strewn.i = t1.i) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: t_strewn.i + -> Seq Scan on t_strewn -> Hash - -> Redistribute Motion 3:3 (slice1; segments: 3) - Hash Key: t_strewn.i - -> Seq Scan on t_strewn + -> Seq Scan on t1 Optimizer: Pivotal Optimizer (GPORCA) (12 rows) diff --git a/src/test/regress/sql/bfv_joins.sql b/src/test/regress/sql/bfv_joins.sql index b82de31a9edd..4f3e0457cc25 100644 --- a/src/test/regress/sql/bfv_joins.sql +++ b/src/test/regress/sql/bfv_joins.sql @@ -740,6 +740,18 @@ select from (values ('')) tmp(b) join lateral drop table t1; +-- test that empty table gets hashed instead of a bigger table +create table big_table(s smallint) distributed replicated; +create table empty_table(i int) distributed replicated; +insert into big_table select generate_series(1, 10000); +analyze big_table; +analyze empty_table; + +explain select * from big_table, empty_table where s = i; + +drop table big_table; +drop table empty_table; + -- Clean up. None of the objects we create are very interesting to keep around. reset search_path; set client_min_messages='warning'; diff --git a/src/test/regress/sql/qp_query_execution.sql b/src/test/regress/sql/qp_query_execution.sql index a0c5b41814b0..8fd6f0542f2d 100644 --- a/src/test/regress/sql/qp_query_execution.sql +++ b/src/test/regress/sql/qp_query_execution.sql @@ -249,7 +249,7 @@ insert into b select i%7, i%10, i , i || 'SOME NUMBER', i % 4 from generate_seri analyze abbp; analyze b; -select qx_count_operator('select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6;', 'Hash Right Join', 'Hash Left Join'); +select qx_count_operator('select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6;', 'Hash Right Join', 'Hash Right Join'); select abbp.k, abbp.t from abbp left outer join b on abbp.k = b.k where abbp.t is not null and abbp.p = 6 order by 1, 2 desc limit 10; select qx_count_operator('select abbp.b, abbp.t from abbp left outer join b on abbp.a = b.k where abbp.t is not null and abbp.a = 6;', 'Hash Right Join', 'Hash Left Join'); From ccc0e4bf3fc19c6a74c0ab26bc45aee4d9bfcc1e Mon Sep 17 00:00:00 2001 From: Viktor Kurilko Date: Mon, 29 Sep 2025 18:24:15 +0700 Subject: [PATCH 010/111] [ORCA] Fix a segmentation fault when append statistics to a group. When appending statistics to a group, it is first checked whether statistics already exist in the group. If it exists, then new statistics are appended to the existing one using the AppendStats method. If there are no statistics in the group, the existence of statistics in the duplicate group is also checked. When appending statistics to an existing one, we take the existing statistics of the group (or its duplicate), create a copy of it, and add statistics to this copy, and release the old one. If the group does not have its own statistics and the duplicate statistics are used. Then we would add statistics to the duplicate group and try to release the statistics of the current group, which is NULL, which leads to a segmentation fault. Fix this by calling the AppendStats method on the duplicate. --- .../gporca/libgpopt/src/search/CGroup.cpp | 6 ++++++ .../src/unittest/gpopt/base/CGroupTest.cpp | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/backend/gporca/libgpopt/src/search/CGroup.cpp b/src/backend/gporca/libgpopt/src/search/CGroup.cpp index ce3853f2e109..3b723bd4d0dd 100644 --- a/src/backend/gporca/libgpopt/src/search/CGroup.cpp +++ b/src/backend/gporca/libgpopt/src/search/CGroup.cpp @@ -862,6 +862,12 @@ CGroup::AppendStats(CMemoryPool *mp, IStatistics *stats) GPOS_ASSERT(NULL != stats); GPOS_ASSERT(NULL != Pstats()); + if (FDuplicateGroup()) + { + PgroupDuplicate()->AppendStats(mp, stats); + return; + } + IStatistics *stats_copy = Pstats()->CopyStats(mp); stats_copy->AppendStats(mp, stats); diff --git a/src/backend/gporca/server/src/unittest/gpopt/base/CGroupTest.cpp b/src/backend/gporca/server/src/unittest/gpopt/base/CGroupTest.cpp index de06032d3716..cd4ba99944d4 100644 --- a/src/backend/gporca/server/src/unittest/gpopt/base/CGroupTest.cpp +++ b/src/backend/gporca/server/src/unittest/gpopt/base/CGroupTest.cpp @@ -104,6 +104,24 @@ CGroupTest::EresUnittest_FResetStatsOnCGroupWithDuplicateGroup() return GPOS_FAILED; } + CStatistics *stat = + GPOS_NEW(mp) CStatistics(mp, GPOS_NEW(mp) UlongToHistogramMap(mp), + GPOS_NEW(mp) UlongToDoubleMap(mp), 0, false); + + IStatistics *oldStats = pmemo->Pgroup(0)->Pstats(); + pmemo->Pgroup(0)->AppendStats(mp, stat); + + // By appending stats on group (0), we really are appending the stats on + // group (1). group (0) stats is never set in the first place. + if (oldStats == pmemo->Pgroup(1)->Pstats()) + { + stat->Release(); + GPOS_DELETE(pmemo); + pexprGet1->Release(); + pexprGet2->Release(); + return GPOS_FAILED; + } + pmemo->Pgroup(0)->FResetStats(); // By resetting stats on group (0), we really are resetting the stats on @@ -111,12 +129,14 @@ CGroupTest::EresUnittest_FResetStatsOnCGroupWithDuplicateGroup() if (pmemo->Pgroup(0)->Pstats() != NULL || pmemo->Pgroup(1)->Pstats() != NULL) { + stat->Release(); GPOS_DELETE(pmemo); pexprGet1->Release(); pexprGet2->Release(); return GPOS_FAILED; } + stat->Release(); GPOS_DELETE(pmemo); pexprGet1->Release(); pexprGet2->Release(); From b5bdddabcd5e517948bbf23e9e0f039d62df05ba Mon Sep 17 00:00:00 2001 From: Viktor Kurilko Date: Mon, 29 Sep 2025 18:51:24 +0700 Subject: [PATCH 011/111] [ORCA] Fix infinite recursion during statistic derivation During the exploration phase, new groups of equivalent expressions are created. In this process, some groups are marked as duplicates. After exploration, expressions from duplicate groups are moved into the group they duplicate. In cases where a duplicate group contains an expression that references the duplicated group, merging them results in a situation where a group contains an expression that references the very group it belongs to. This leads to infinite recursion during statistics derivation. The fix is to improve the cycle-detection logic so that it can recognize when an expression references the group it resides in. --- .../CTEMergeGroupsCircularDeriveStats2.mdp | 595 ++++++++++++++++++ .../libgpopt/src/search/CGroupExpression.cpp | 18 + .../src/unittest/gpopt/minidump/CCTETest.cpp | 1 + 3 files changed, 614 insertions(+) create mode 100644 src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats2.mdp diff --git a/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats2.mdp b/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats2.mdp new file mode 100644 index 000000000000..6eb9bdb457df --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats2.mdp @@ -0,0 +1,595 @@ + + + Hash Join + Hash Cond: (t1.c = t3.a) + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: t1.c + -> Hash Join + Hash Cond: (t1.a = t2.b) + -> Seq Scan on t1 + -> Hash + -> Broadcast Motion 3:3 (slice1; segments: 3) + -> Seq Scan on t2 + -> Hash + -> Redistribute Motion 3:3 (slice3; segments: 3) + Hash Key: t3.a + -> Seq Scan on t3 + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/libgpopt/src/search/CGroupExpression.cpp b/src/backend/gporca/libgpopt/src/search/CGroupExpression.cpp index 34ad3dad91da..96283422d732 100644 --- a/src/backend/gporca/libgpopt/src/search/CGroupExpression.cpp +++ b/src/backend/gporca/libgpopt/src/search/CGroupExpression.cpp @@ -1104,6 +1104,16 @@ CGroupExpression::OsPrintCostContexts(IOstream &os, const CHAR *szPrefix) const // 4: CLogicalInnerJoin [ 6 7 3 ] Origin: (xform: CXformExpandNAryJoinGreedy, Grp: 4, GrpExpr: 3) // // Group 0 (#GExprs: 0, Duplicate Group: 4): +// +// There is also a chance that one of the group's expressions refers to the same group. +// This can happen if one of the groups has a link to another group, which in turn is its duplicate. +// Example: Group 0 is a duplicate of group 12, while it has an expression 1 +// that refers to group 12. After the groups merge, this expression will end up +// in group 12, which will lead to a cyclical relationship. +// Group 0 (#GExprs: 3, Duplicate Group: 12): +// 0: CLogicalCTEConsumer (1), Columns: ["a" (98), "b" (99), "c" (100)] [ ] +// 1: CLogicalSelect [ 12 3 ] +// 2: CLogicalNAryJoin [ 13 14 15 ] BOOL CGroupExpression::ContainsCircularDependencies() { @@ -1122,6 +1132,14 @@ CGroupExpression::ContainsCircularDependencies() CGroup *child_group = (*child_groups)[ul]; if (child_group->FScalar()) continue; + + if (child_group->Id() == m_pgroup->Id()) + { + m_ecirculardependency = CGroupExpression::ecdCircularDependency; + GPOS_ASSERT(Pgroup()->UlGExprs() > 1); + break; + } + CGroup *child_duplicate_group = child_group->PgroupDuplicate(); if (child_duplicate_group != NULL) { diff --git a/src/backend/gporca/server/src/unittest/gpopt/minidump/CCTETest.cpp b/src/backend/gporca/server/src/unittest/gpopt/minidump/CCTETest.cpp index 53cabdf2ac5b..7e040abc863b 100644 --- a/src/backend/gporca/server/src/unittest/gpopt/minidump/CCTETest.cpp +++ b/src/backend/gporca/server/src/unittest/gpopt/minidump/CCTETest.cpp @@ -59,6 +59,7 @@ const CHAR *rgszCTEFileNames[] = { "../data/dxl/minidump/CTE-ValuesScan-ProjList.mdp", "../data/dxl/minidump/CTEWithMergedGroup.mdp", "../data/dxl/minidump/CTEMergeGroupsCircularDeriveStats.mdp", + "../data/dxl/minidump/CTEMergeGroupsCircularDeriveStats2.mdp", "../data/dxl/minidump/CTE15Replicated.mdp", "../data/dxl/minidump/CTE2Replicated.mdp", "../data/dxl/minidump/CTE15HAReplicated.mdp", From 5944707681dbcf4cfab3bc19b17ffb38cf5cdf8b Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Tue, 7 Oct 2025 16:48:54 +0700 Subject: [PATCH 012/111] Add building a deb package feature (#31) - New CI Job for auto build deb-package - New targets for gpAux Makefile: changelog, changelog-deb, pkg, pkg-deb - New gpAux/debian folder with package description/rules for `debuild` utility - Copy `VERSION` file from source to main layer in Docker Image - Disable clean `VERSION` file if `.git` directory not exists - Deb package name gets from `Package` field in `gpAux/debian/control` file Ticket: ADBDEV-7873 --- .github/workflows/greengage-ci.yml | 33 ++++++-- .gitignore | 5 ++ README.ubuntu.bash | 4 + ci/Dockerfile.ubuntu | 1 + gpAux/BUILD_INSTRUCTIONS | 18 ++++ gpAux/Makefile | 109 ++++++++++++++++++------ gpAux/README.package.md | 131 +++++++++++++++++++++++++++++ gpAux/debian/compat | 1 + gpAux/debian/control | 30 +++++++ gpAux/debian/copyright | 24 ++++++ gpAux/debian/lintian-overrides | 9 ++ gpAux/debian/rules | 40 +++++++++ 12 files changed, 374 insertions(+), 31 deletions(-) create mode 100644 gpAux/README.package.md create mode 100644 gpAux/debian/compat create mode 100644 gpAux/debian/control create mode 100644 gpAux/debian/copyright create mode 100644 gpAux/debian/lintian-overrides create mode 100755 gpAux/debian/rules diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index 7e5fbbc7c41b..ebc1213dfa16 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -18,12 +18,12 @@ jobs: strategy: fail-fast: true # Stop on any failure in the matrix matrix: - target_os: [ubuntu] #, centos] + target_os: [ubuntu] permissions: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-build.yml@v1 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-build.yml@v3 with: version: 6 target_os: ${{ matrix.target_os }} @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: true matrix: - target_os: [ubuntu] #, centos] + target_os: [ubuntu] permissions: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity @@ -54,7 +54,7 @@ jobs: strategy: fail-fast: true matrix: - target_os: [ubuntu] #, centos] + target_os: [ubuntu] permissions: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity @@ -72,7 +72,7 @@ jobs: strategy: fail-fast: true matrix: - target_os: [ubuntu] #, centos] + target_os: [ubuntu] permissions: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity @@ -90,7 +90,7 @@ jobs: strategy: fail-fast: true matrix: - target_os: [ubuntu] #, centos] + target_os: [ubuntu] permissions: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity @@ -108,7 +108,7 @@ jobs: strategy: fail-fast: true matrix: - target_os: [ubuntu] #, centos] + target_os: [ubuntu] permissions: contents: read # Explicit for default behavior packages: write # Required for GHCR access @@ -121,3 +121,22 @@ jobs: ghcr_token: ${{ secrets.GITHUB_TOKEN }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + + # Rebuild prod-redy version without debug extensions and pack it to deb + package: + needs: build # For push (main or tags) + strategy: + fail-fast: false + matrix: + target_os: [ubuntu] + permissions: + contents: read # Explicit for default behavior + packages: write # Required for GHCR access + actions: write # Required for artifact upload + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-package.yml@v7 + with: + version: 6 + target_os: ${{ matrix.target_os }} + test_docker: ubuntu:22.04 # Docker Image (e.g., ubuntu:22.04, ubuntu:noble) for deploy test. Skip if empty + secrets: + ghcr_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 8f979721c160..799206a138da 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,10 @@ GTAGS lib*dll.def lib*.pc compile_commands.json +**/debian/* +!**/debian/co* +!**/debian/lint* +!**/debian/rules # Local excludes in root directory /GNUmakefile @@ -65,3 +69,4 @@ compile_commands.json /Debug/ /Release/ /CMakeLists.txt +/Package/ diff --git a/README.ubuntu.bash b/README.ubuntu.bash index b740a108eb8d..0042dc4e04bd 100755 --- a/README.ubuntu.bash +++ b/README.ubuntu.bash @@ -3,7 +3,11 @@ apt-get update apt-get install -y \ bison \ + build-essential \ cmake \ + debhelper \ + devscripts \ + dh-python \ curl \ flex \ g++ \ diff --git a/ci/Dockerfile.ubuntu b/ci/Dockerfile.ubuntu index 49098bc0f007..c97b643f859e 100644 --- a/ci/Dockerfile.ubuntu +++ b/ci/Dockerfile.ubuntu @@ -48,6 +48,7 @@ RUN rm -rf gpdb_src/.git/ FROM base as test COPY --from=code /home/gpadmin/gpdb_src gpdb_src COPY --from=build /home/gpadmin/bin_gpdb /home/gpadmin/bin_gpdb +COPY --from=build /home/gpadmin/gpdb_src/VERSION gpdb_src # Install entab used by pgindent utility. # This should be done using gpdb sources. diff --git a/gpAux/BUILD_INSTRUCTIONS b/gpAux/BUILD_INSTRUCTIONS index 6c2f367af187..5219b9b5c0a3 100644 --- a/gpAux/BUILD_INSTRUCTIONS +++ b/gpAux/BUILD_INSTRUCTIONS @@ -23,6 +23,13 @@ You will need the following tools: perl 5.8.8 tar (GNU) 1.14 +To build a Debian package, you will also need the following tools: + + build-essential Debian/Ubuntu package for essential build tools + devscripts Debian/Ubuntu package for Debian packaging scripts + debhelper Debian/Ubuntu package for Debian package helper scripts + dh-python Debian/Ubuntu package for Python-specific Debian packaging + To build the Greengage Database, run GNU make with the following options: ## -------------------- @@ -45,3 +52,14 @@ To build the Greengage Database, run GNU make with the following options: > make GPROOT=`pwd` BLD_TARGETS="clients loaders connectivity gppkg" dist ## ---------------------------------------------------------------------- +## To build a Debian package: +## ---------------------------------------------------------------------- + + > make pkg-deb + +This will generate a changelog, build the Debian package using `debuild`, +and place the resulting `.deb` packages in the parent directory. + +See [README.package.md](README.package.md) + +## ---------------------------------------------------------------------- diff --git a/gpAux/Makefile b/gpAux/Makefile index f861f61f8958..2513a5ae2eb1 100644 --- a/gpAux/Makefile +++ b/gpAux/Makefile @@ -170,34 +170,34 @@ RECONFIG : rm -f $(GPPGDIR)/GNUmakefile $(GPPGDIR)/GNUmakefile : $(GPPGDIR)/configure env.sh - rm -rf $(DESTDIR)$(INSTLOC) + rm -rf $(INSTLOC) mkdir -p $(GPPGDIR) echo "Running ./configure with CONFIGFLAGS=$(CONFIGFLAGS)\n" cd $(GPPGDIR) && CC="$(strip $(BLD_CC) $(BLD_CFLAGS))" \ CFLAGS=$(INSTCFLAGS) \ ./configure $(CONFIGFLAGS) \ - --prefix=$(INSTLOC) \ - --mandir=$(INSTLOC)/man + --prefix=$(DISTPATH) \ + --mandir=$(DISTPATH)/man Debug/GNUmakefile : $(GPPGDIR)/configure env.sh - rm -rf $(DESTDIR)$(INSTLOC) + rm -rf $(INSTLOC) mkdir -p Debug echo "Running ./configure with CONFIGFLAGS=$(CONFIGFLAGS)\n" cd Debug && CC="$(strip $(BLD_CC) $(BLD_CFLAGS))" \ CFLAGS=$(INSTCFLAGS) \ ./configure $(CONFIGFLAGS) \ - --prefix=$(INSTLOC) \ - --mandir=$(INSTLOC)/man + --prefix=$(DISTPATH) \ + --mandir=$(DISTPATH)/man Release/GNUmakefile : $(GPPGDIR)/configure env.sh - rm -rf $(DESTDIR)$(INSTLOC) + rm -rf $(INSTLOC) mkdir -p Release echo "Running ./configure with CONFIGFLAGS=$(CONFIGFLAGS)\n" cd Release && CC="$(strip $(BLD_CC) $(BLD_CFLAGS))" \ CFLAGS=$(INSTCFLAGS) \ ./configure $(CONFIGFLAGS) \ - --prefix=$(INSTLOC) \ - --mandir=$(INSTLOC)/man + --prefix=$(DISTPATH) \ + --mandir=$(DISTPATH)/man #--------------------------------------------------------------------- # autoconf @@ -224,7 +224,7 @@ perl_archlibexp:=$(shell perl -MConfig -e 'print $$Config{archlibexp}') # set default build steps define BUILD_STEPS - @rm -rf $(DESTDIR)$(INSTLOC) + @rm -rf $(INSTLOC) cd $(BUILDDIR) && PYGRESQL_LDFLAGS='-Wl,-rpath,\$$$$ORIGIN/../../../lib -Wl,--enable-new-dtags' $(MAKE) $(PARALLEL_MAKE_OPTS) install cd $(BUILDDIR)/src/pl/plpython && $(MAKE) clean && $(MAKE) $(PARALLEL_MAKE_OPTS) install && cd $(BUILDDIR) cd $(BUILDDIR)/src/pl/plperl && $(MAKE) clean && echo "LDFLAGS += -Wl,-rpath,$(perl_archlibexp)/CORE -Wl,--enable-new-dtags" >> GNUmakefile && echo "LDFLAGS_SL += -Wl,-rpath,$(perl_archlibexp)/CORE -Wl,--enable-new-dtags" >> GNUmakefile && $(MAKE) $(PARALLEL_MAKE_OPTS) install && cd $(BUILDDIR) @@ -241,7 +241,7 @@ endef ifeq "$(BLD_GPDB_BUILDSET)" "partial" define BUILD_STEPS - rm -rf $(DESTDIR)$(INSTLOC) + rm -rf $(INSTLOC) cd $(BUILDDIR)/gpMgmt/ && $(MAKE) generate_greengage_path_file cd $(BUILDDIR)/src/backend/ && $(MAKE) ../../src/include/parser/gram.h cd $(BUILDDIR)/src/backend/ && $(MAKE) ../../src/include/utils/errcodes.h @@ -290,8 +290,8 @@ endif #--------------------------------------------------------------------- devel : INSTCFLAGS=$(DEBUGFLAGS) -devel : INSTLOC=$(DEVPATH) -devel : CLIENTSINSTLOC=$(CLIENTSDEVPATH) +devel : INSTLOC=$(DESTDIR)$(DEVPATH) +devel : CLIENTSINSTLOC=$(DESTDIR)$(CLIENTSDEVPATH) devel : CONFIGFLAGS+= --enable-cassert --enable-debug --enable-depend ifdef ENABLE_VPATH_BUILD devel : BUILDDIR=Debug @@ -307,8 +307,8 @@ endif #--------------------------------------------------------------------- dist : INSTCFLAGS=$(OPTFLAGS) -dist : INSTLOC=$(DISTPATH) -dist : CLIENTSINSTLOC=$(CLIENTSDISTPATH) +dist : INSTLOC=$(DESTDIR)$(DISTPATH) +dist : CLIENTSINSTLOC=$(DESTDIR)$(CLIENTSDISTPATH) ifdef ENABLE_VPATH_BUILD dist : BUILDDIR=Release dist : ISCONFIG=$(BUILDDIR)/GNUmakefile @@ -319,8 +319,8 @@ endif $(BUILD_STEPS) dist_prof : INSTCFLAGS=$(PROFFLAGS) -dist_prof : INSTLOC=$(DISTPATH) -dist_prof : CLIENTSINSTLOC=$(CLIENTSDISTPATH) +dist_prof : INSTLOC=$(DESTDIR)$(DISTPATH) +dist_prof : CLIENTSINSTLOC=$(DESTDIR)$(CLIENTSDISTPATH) ifdef ENABLE_VPATH_BUILD dist_prof : BUILDDIR=Release dist_prof : ISCONFIG=$(BUILDDIR)/GNUmakefile @@ -331,7 +331,7 @@ dist_prof : gccVersionCheck GPROOTDEP RECONFIG $(ISCONFIG) endif $(BUILD_STEPS) -dist_faultinj : CLIENTSINSTLOC=$(CLIENTSDISTPATH) +dist_faultinj : CLIENTSINSTLOC=$(DESTDIR)$(CLIENTSDISTPATH) ifdef ENABLE_VPATH_BUILD dist_faultinj : BUILDDIR=Release dist_faultinj : ISCONFIG=$(BUILDDIR)/GNUmakefile @@ -342,6 +342,67 @@ dist_faultinj : gccVersionCheck GPROOTDEP RECONFIG $(ISCONFIG) endif $(BUILD_STEPS) +#--------------------------------------------------------------------- +# Packaging targets with changelog options +#--------------------------------------------------------------------- + +# Metadata vars +PACKAGE_NAME := $(shell grep '^Package:' debian/control | head -1 | awk '{print $$2}') +MAINTAINER := $(shell grep '^Maintainer:' debian/control | sed 's/Maintainer: //') +DATE_RFC := $(shell date -R) +ARTIFACTS_DIR := $(CURDIR)/../Package + +../VERSION : + @echo "Update $@" + ../getversion > $@ + @cat $@ + +version-vars : ../VERSION + $(eval FULL_VERSION := $(shell [ -f ../VERSION ] && perl -pe 's, ,-,g' ../VERSION)) + $(eval PACKAGE_VERSION := $(shell [ -f ../VERSION ] && perl -pe 's, .*,,g' ../VERSION)) + $(eval IS_RELEASE := $(if $(findstring +dev,$(PACKAGE_VERSION)),no,yes)) + $(eval STABILITY := $(if $(filter yes,$(IS_RELEASE)),stable,unstable)) + $(eval BUILD_TYPE := $(if $(filter yes,$(IS_RELEASE)),Release build,Development build)) + +version-info : version-vars + @echo "PACKAGE_VERSION: $(PACKAGE_VERSION)" + @echo "FULL_VERSION: $(FULL_VERSION)" + @echo "IS_RELEASE: $(IS_RELEASE)" + @echo "STABILITY: $(STABILITY)" + @echo "BUILD_TYPE: $(BUILD_TYPE)" + +# Generate package control files +changelog : debian/changelog +debian/changelog : version-vars + @echo "$(PACKAGE_NAME) ($(PACKAGE_VERSION)) $(STABILITY); urgency=low" > $@ + @echo "" >> $@ + @echo " * $(BUILD_TYPE)" >> $@ + @echo "" >> $@ + @echo " -- $(MAINTAINER) $(DATE_RFC)" >> $@ + +debian/install: + @echo "$(PACKAGE_NAME)/* /" > $@ + + +# Default packaging target +pkg : pkg-deb + +# Build Debian package +pkg-deb : GPROOT = /opt/greengagedb +pkg-deb : GPDIR = $(PACKAGE_NAME) +pkg-deb : debian/changelog debian/install + @echo "Building with GPROOT=$(GPROOT), GPDIR=$(GPDIR), PACKAGE_NAME=$(PACKAGE_NAME)" + @GPROOT="$(GPROOT)" GPDIR="$(GPDIR)" PACKAGE_NAME="$(PACKAGE_NAME)" debuild --preserve-env -us -uc -b + @mkdir -p $(ARTIFACTS_DIR) + @find $(CURDIR)/../ -maxdepth 1 -type f \( -name "*.deb" \ + -o -name "*.ddeb" \ + -o -name "*.build" \ + -o -name "*.buildinfo" \ + -o -name "*.changes" \) \ + -exec mv -f {} $(ARTIFACTS_DIR)/ \; + +.PHONY: pkg pkg-deb changelog version-vars version-info + #--------------------------------------------------------------------- # clientTools #--------------------------------------------------------------------- @@ -412,7 +473,7 @@ endif endif ifeq "$(INSTLOC)" "" -INSTLOC=$(GPDIR) +INSTLOC=$(DESTDIR)$(GPDIR) endif VERSION:=$(shell [ -f ../VERSION ] && perl -pe 's, ,-,g' ../VERSION) @@ -473,7 +534,7 @@ PVK_FILESET = \ $(NULL) pvk : INSTCFLAGS=$(OPTFLAGS) -pvk : INSTLOC=$(DISTPATH) +pvk : INSTLOC=$(DESTDIR)$(DISTPATH) pvk : gccVersionCheck version GPROOTDEP cd $(GPMGMT)/bin && $(MAKE) stream mkdir -p $(INSTLOC)/bin/lib @@ -539,7 +600,7 @@ ifeq "$(shell uname -s)" "Darwin" echo "pgbouncer can't build on Mac" else @if [ ! -f extensions/pgbouncer/source/configure ]; then cd extensions/pgbouncer/source && ./autogen.sh ;fi - @cd extensions/pgbouncer/source && ./configure --with-libevent=$(BLD_TOP)/ext/$(BLD_ARCH) --prefix=$(INSTLOC) --enable-evdns --with-pam --with-ldap + @cd extensions/pgbouncer/source && ./configure --with-libevent=$(BLD_TOP)/ext/$(BLD_ARCH) --prefix=$(DISTPATH) --enable-evdns --with-pam --with-ldap $(MAKE) -C extensions/pgbouncer/source install endif @@ -618,8 +679,8 @@ copylibs : mkdir -p $(INSTLOC)/include greengage_path: - mkdir -p $(DESTDIR)$(INSTLOC) - $(BUILDDIR)/gpMgmt/bin/generate-greengage-path.sh no > $(DESTDIR)$(INSTLOC)/greengage_path.sh + mkdir -p $(INSTLOC) + $(BUILDDIR)/gpMgmt/bin/generate-greengage-path.sh no > $(INSTLOC)/greengage_path.sh copylicense: for proddir in $(INSTLOC) $(CLIENTSINSTLOC); do \ @@ -669,7 +730,7 @@ distclean : @if [ -d Release -a -f Release/GNUmakefile ]; then cd Release; $(MAKE) distclean; fi @if [ -d $(GPPGDIR) -a -f $(GPPGDIR)/GNUmakefile ]; then cd $(GPPGDIR); $(MAKE) distclean; fi @if [ -d $(GPMGMT)/bin ]; then cd $(GPMGMT)/bin; $(MAKE) distclean; fi - @rm -f VERSION + @if [ -d .git ] ; then rm -f VERSION; fi @rm -f env.sh distprep : $(ISCONFIG) diff --git a/gpAux/README.package.md b/gpAux/README.package.md new file mode 100644 index 000000000000..14e82f666ec8 --- /dev/null +++ b/gpAux/README.package.md @@ -0,0 +1,131 @@ +# Greengage Database Packaging System + +## Overview + +This documentation describes the Debian packaging system for Greengage Database located in the `gpAux/` subdirectory. The system builds Debian packages using a custom Makefile and `debian/rules` file. + +## Location and Structure + +The packaging system is located in: + +```text +./gpAux/ +``` + +The main components are: + +- `Makefile` - Defines packaging targets, version management, and artifact collection +- `debian/rules` - Debian build rules with custom overrides +- `debian/control` - Package metadata and dependencies +- Other standard Debian packaging files + +## Key Components + +### Makefile Targets + +1. **Version Management**: + - `../VERSION`: Generates version file using `../getversion` + - `version-vars`: Sets build variables (`FULL_VERSION`, `PACKAGE_VERSION`, `IS_RELEASE`, `STABILITY`, `BUILD_TYPE`) from `../VERSION` + - `version-info`: Displays version information for debugging + +2. **Packaging Targets**: + - `pkg`: Default target (aliases to `pkg-deb`) + - `pkg-deb`: Builds Debian package, preserves environment variables, and collects specific artifacts (`.deb`, `.ddeb`, `.build`, `.buildinfo`, `.changes`) + - `changelog`: Generates `debian/changelog` using version variables + - `debian/install`: Creates installation manifest + +### Debian Rules File + +The `debian/rules` file uses debhelper (dh) with custom overrides: + +1. **Distribution-specific Dependencies**: + - Detects Ubuntu 22.04 and adds `python2.7` dependency + +2. **Build Process Overrides**: + - Skips standard configure and build steps + - Uses the project's `make dist` target for installation + - Unsets standard compiler flags to avoid conflicts + - Enables parallel builds using all available CPU cores + +3. **Control File Generation**: + - Adds Python dependencies for Ubuntu 22.04 + +## Usage + +### Building the Package + +From the project root directory, run: + +```bash +make -C ./gpAux pkg-deb +``` + +### Custom Installation Paths + +To customize installation paths, set environment variables: + +```bash +make -C ./gpAux pkg-deb GGROOT=/custom/path GPDIR=custom_dir +``` + +### Environment Variables + +- `GGROOT`: Installation root directory (default: `/opt/greengagedb`) +- `GPDIR`: Subdirectory under `GGROOT` (default: `` from `debian/control`) +- `ARTIFACTS_DIR`: Directory for build artifacts (default: `$(CURDIR)/../Package`) + +## Build Process Details + +1. **Version Generation**: + - Runs `../getversion` to create `../VERSION` + - Processes version string into `FULL_VERSION` and `PACKAGE_VERSION` + - Sets `IS_RELEASE` and `STABILITY` for changelog generation + +2. **Package Building**: + - Executes `debuild` with preserved environment variables (`GGROOT`, `GPDIR`, `PACKAGE_NAME`) + - Skips signing with `-us -uc` flags + - Collects specific build artifacts (`.deb`, `.ddeb`, `.build`, `.buildinfo`, `.changes`) into `ARTIFACTS_DIR` + +3. **Installation**: + - Uses `make dist` for installation into `debian/tmp` + - Generates file manifest in `debian/install` + +## Dependencies + +The packaging system automatically handles: + +- `Ubuntu 22.04` detection and `python2.7` dependency +- Other distributions may require manual dependency configuration + +## Maintenance + +### Updating Package Metadata + +Edit `debian/control` to update: + +- Package description +- Maintainer information +- General dependencies + +### Adding Distribution Support + +Modify distribution detection in `debian/rules`: + +```makefile +ifeq ($(LSB_SI),Ubuntu) + ifeq ($(LSB_SR),22.04) + DEPS=python2.7 + endif + ifeq ($(LSB_SR),20.04) + DEPS=python2.7 + endif +endif +``` + +## Notes + +- Skips tests (`DEB_BUILD_OPTIONS=nocheck`) for faster builds +- Unsets compiler flags to avoid conflicts with the project's build system +- Enables parallel builds using all available CPU cores +- Builds without signing for development convenience +- Collects only specific build artifacts (`.deb`, `.ddeb`, `.build`, `.buildinfo`, `.changes`) into `$(CURDIR)/../Package` diff --git a/gpAux/debian/compat b/gpAux/debian/compat new file mode 100644 index 000000000000..b1bd38b62a08 --- /dev/null +++ b/gpAux/debian/compat @@ -0,0 +1 @@ +13 diff --git a/gpAux/debian/control b/gpAux/debian/control new file mode 100644 index 000000000000..760b24b0f548 --- /dev/null +++ b/gpAux/debian/control @@ -0,0 +1,30 @@ +Source: greengage +Maintainer: Greengage +Section: database +Build-Depends: debhelper (>= 13), + dh-python, + lsb-release + +Package: greengage +Architecture: any +Depends: ${shlibs:Depends}, + iproute2, + iputils-ping, + less, + openssh-client, + openssh-server, + openssl, + python2, + rsync, + zip, + net-tools, + ${pythonRequires} +Conflicts: greengage-loaders, + python-is-python3 +Description: Greengage MPP database engine + Greengage Database (GPDB) is an advanced, fully featured, open + source data warehouse, based on PostgreSQL. It provides powerful and + rapid analytics on petabyte scale data volumes. Uniquely geared toward + big data analytics, Greengage Database is powered by the world’s most + advanced cost-based query optimizer delivering high analytical query + performance on large data volumes. diff --git a/gpAux/debian/copyright b/gpAux/debian/copyright new file mode 100644 index 000000000000..cdc1800d9a0a --- /dev/null +++ b/gpAux/debian/copyright @@ -0,0 +1,24 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: greengage +Upstream-Contact: GreengageDB Team +Source: https://github.com/GreengageDB/greengage + +Files: * +Copyright: See COPYRIGHT file +License: Apache-2.0 + +License: Apache-2.0 + 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. + . + On Debian systems, the complete text of the Apache License 2.0 can be + found in "/usr/share/common-licenses/Apache-2.0". diff --git a/gpAux/debian/lintian-overrides b/gpAux/debian/lintian-overrides new file mode 100644 index 000000000000..491c4105a411 --- /dev/null +++ b/gpAux/debian/lintian-overrides @@ -0,0 +1,9 @@ +# 3rd-party Package +dir-or-file-in-opt + +# TODO +unusual-interpreter python +script-uses-unversioned-python-in-shebang + +# Not applicable for plugins +shared-library-lacks-prerequisites diff --git a/gpAux/debian/rules b/gpAux/debian/rules new file mode 100755 index 000000000000..878508a6da3b --- /dev/null +++ b/gpAux/debian/rules @@ -0,0 +1,40 @@ +#!/usr/bin/make -f +# Run command: `make -C ./gpdb_src/gpAux pkg-deb` + +DH_VERBOSE = 1 +export DH_OPTIONS = -v +export DEB_BUILD_OPTIONS = nocheck + +LSB_SI := $(shell lsb_release -si) +LSB_SR := $(shell lsb_release -sr) + +ifeq ($(LSB_SI),Ubuntu) + ifeq ($(LSB_SR),22.04) + DEPS = python2.7 + endif +endif + +%: + dh $@ + +override_dh_auto_clean: + @echo "Skipping clean" + +override_dh_auto_configure: + @echo "Skipping configure" + +override_dh_auto_build: + @echo "Skipping autobuild" + +override_dh_auto_install: + @echo "=== Building and installing with GPROOT=$(GPROOT), GPDIR=$(GPDIR), PACKAGE_NAME=$(PACKAGE_NAME) ===" + env -u CFLAGS -u CPPFLAGS -u CXXFLAGS -u LDFLAGS \ + $(MAKE) dist \ + DESTDIR=$(CURDIR)/debian/tmp/$(PACKAGE_NAME) \ + GPROOT=$(GPROOT) \ + GPDIR=$(GPDIR) \ + PARALLEL_MAKE_OPTS=-j$(shell nproc) + +override_dh_gencontrol: + @echo "=== GENCONTROL GPROOT=$(GPROOT), GPDIR=$(GPDIR), PACKAGE_NAME=$(PACKAGE_NAME) ===" + dh_gencontrol -p$(PACKAGE_NAME) -- -VpythonRequires="$(DEPS)" From 95df356ce5c45ef2123f6dcdbd5ae4dfc6d739f3 Mon Sep 17 00:00:00 2001 From: Dmitry Voronkov Date: Tue, 7 Oct 2025 15:18:53 +0300 Subject: [PATCH 013/111] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 17 ++++++++++ 2 files changed, 55 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000000..5405f5e2154c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[Bug] " +labels: '' +assignees: '' + +--- + +**Describe the problem** + +Please describe the issue you observed, and any steps we can take to reproduce it: + +**How to reproduce** + +Steps to reproduce the behavior: + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Additional data** +If the problem is SQL-related, include a copy of the SQL query and the schema. + +If a segment in your cluster encountered a fatal error, supply the contents of the +log directory (at minimum of the affected segment(s), but preferably all segments). + +Note that log files can contain confidential information. + + +**Environment:** + - GreengageDB version [e.g. 6.29.x] + - Server OS: [e.g. Linux/Distrib version] + - Client application [e.g. `psql`, `JDBC`, `pxf`, ...] + +**Additional context** +What was the impact? + +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000000..f61d70e28ade --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for Greengage project +title: "[Feature]" +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. + +**Describe the solution** +A clear and concise description of what you want to happen. + +**Additional context** +Add any other context about the feature request, user stories or examples of usage. From 43b119a4e421b34ee0a45dbceda5e00f8b7d7b2e Mon Sep 17 00:00:00 2001 From: dimoffon Date: Wed, 8 Oct 2025 09:57:05 +0300 Subject: [PATCH 014/111] change the format of bug_report to yml --- .github/ISSUE_TEMPLATE/bug_report.md | 38 ------------ .github/ISSUE_TEMPLATE/bug_report.yml | 84 +++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 38 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 5405f5e2154c..000000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: "[Bug] " -labels: '' -assignees: '' - ---- - -**Describe the problem** - -Please describe the issue you observed, and any steps we can take to reproduce it: - -**How to reproduce** - -Steps to reproduce the behavior: - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Additional data** -If the problem is SQL-related, include a copy of the SQL query and the schema. - -If a segment in your cluster encountered a fatal error, supply the contents of the -log directory (at minimum of the affected segment(s), but preferably all segments). - -Note that log files can contain confidential information. - - -**Environment:** - - GreengageDB version [e.g. 6.29.x] - - Server OS: [e.g. Linux/Distrib version] - - Client application [e.g. `psql`, `JDBC`, `pxf`, ...] - -**Additional context** -What was the impact? - -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000000..aca16a9294a0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,84 @@ +name: Bug Report +description: Create a report to help us improve GreengageDB +title: "[Bug] " +labels: ["bug"] +assignees: [] + +body: + - type: textarea + id: problem_description + attributes: + label: "Describe the problem" + description: "Please describe the issue you observed, and any steps we can take to reproduce it" + placeholder: "Describe the problem you encountered..." + validations: + required: true + + - type: textarea + id: reproduction_steps + attributes: + label: "How to reproduce" + description: "Steps to reproduce the behavior" + placeholder: | + 1. + 2. + 3. + ... + validations: + required: true + + - type: textarea + id: expected_behavior + attributes: + label: "Expected behavior" + description: "A clear and concise description of what you expected to happen" + placeholder: "Describe what you expected to happen..." + validations: + required: true + + - type: textarea + id: additional_data + attributes: + label: "Additional data" + description: | + If the problem is SQL-related, include a copy of the SQL query and the schema. + + If a segment in your cluster encountered a fatal error, supply the contents of the log directory (at minimum of the affected segment(s), but preferably all segments). + + Note that log files can contain confidential information. + placeholder: | + SQL Query: + Schema: + Log files: + ... + + - type: input + id: environment_greengagedb + attributes: + label: "GreengageDB version" + description: "What version of GreengageDB are you using?" + validations: + required: true + + - type: input + id: environment_os + attributes: + label: "Server OS" + description: "What operating system is the server running on?" + placeholder: "e.g. Linux/Distrib version" + validations: + required: true + + - type: input + id: environment_client + attributes: + label: "Client application" + description: "What client application are you using?" + placeholder: "e.g. psql, JDBC, pxf, ..." + + - type: textarea + id: additional_context + attributes: + label: "Additional context" + description: "What was the impact? Add any other context about the problem here" + placeholder: "Add any other relevant context about the problem..." \ No newline at end of file From eaa3405c2a555faabe574d62fe73dc29154fbd1a Mon Sep 17 00:00:00 2001 From: Dmitry Voronkov Date: Wed, 8 Oct 2025 16:00:11 +0300 Subject: [PATCH 015/111] Update CONTRIBUTING.md (#82) Updated links to refer to the main branch --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 85a4e96f3d1e..a557b7874723 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ A private copy of the Greengage repository is required to introduce changes. To As the original author of the code, you can expect that your contribution will be released and licensed under Apache License, v. 2.0. Additionally, certain contributions valuable to the broader PostgreSQL community might be released under the PostgreSQL license. If your patch is beneficial for upstream PostgreSQL, we can offer it for review individually or include it with a set of changes. -If you are NOT the author of the code you are contributing to Greengage, please make sure you take proper licensing into account. Check the third-party license terms for similarity to the Apache License 2.0. Similar licenses are listed on the Apache Software Foundation website under [Category A](https://www.apache.org/legal/resolved.html#category-a). Note that some of these licenses require making proper attribution in the [NOTICE file](https://github.com/GreengageDB/greengage/blob/adb-6.x/NOTICE) (see examples [here](https://github.com/GreengageDB/greengage/blob/adb-6.x/NOTICE#L278)). +If you are NOT the author of the code you are contributing to Greengage, please make sure you take proper licensing into account. Check the third-party license terms for similarity to the Apache License 2.0. Similar licenses are listed on the Apache Software Foundation website under [Category A](https://www.apache.org/legal/resolved.html#category-a). Note that some of these licenses require making proper attribution in the [NOTICE file](https://github.com/GreengageDB/greengage/blob/main/NOTICE) (see examples [here](https://github.com/GreengageDB/greengage/blob/main/NOTICE#L335)). Do NOT remove licensing headers from any piece of work done by a third party. Even partial usage of someone else’s work may assume licensing implications. Please give the original author credit for their work by keeping the licensing headers @@ -74,4 +74,4 @@ While the process is generally intuitive and enables you understand what exactly ## Direct commits to the repository -Members of the architectural committee may sometimes commit to the repository directly, without submitting pull requests. Usually they do so to introduce minor changes (i.e. typo corrections), all major code contributions need to be submitted as pull requests and go through checks. \ No newline at end of file +Members of the architectural committee may sometimes commit to the repository directly, without submitting pull requests. Usually they do so to introduce minor changes (i.e. typo corrections), all major code contributions need to be submitted as pull requests and go through checks. From 8a2a4716b8eb2d40a14ea0c727835f38b97cbadf Mon Sep 17 00:00:00 2001 From: Vladimir Sarmin Date: Mon, 13 Oct 2025 12:44:32 +0300 Subject: [PATCH 016/111] Track PQresult allocations in server side (#45) Prior to 3ce2e6a, querying pg_locks (or using pg_lock_status()), approximately 75% of backend memory allocations for resulting tuples weren't registered with Vmtracker or Resource Group Control. This memory would also leak if the query was cancelled or failed. This happened because CdbDispatchCommand(), which was previously used by pg_locks, called libpq to obtain the results that were allocated as PQresult structures with bare malloc(), even on the server side. This patch fixes both untracked memory issues by enforcing Vmtracker routines for PGresult allocations on the server-side. Including postgres.h in frontend code causes several errcode-related macro redefinition warnings. They are now un-definined first. Recursive errors due to mishandled OOM errors are addressed in c4e1085. This PR also adds an additional set of tests, building on top of the said commit. Ticket: ADBDEV-7691 --- src/backend/cdb/dispatcher/cdbdisp_async.c | 2 + src/interfaces/libpq/fe-connect.c | 18 +++ src/interfaces/libpq/fe-exec.c | 32 ++--- src/interfaces/libpq/fe-protocol3.c | 6 +- src/interfaces/libpq/libpq-int.h | 28 +++++ .../fault_inject.source | 25 ++++ .../input/resgroup/resgroup_oom.source | 53 ++++++++ .../isolation2/isolation2_resgroup_schedule | 2 + .../fault_inject.source | 44 +++++++ .../output/resgroup/resgroup_oom.source | 66 ++++++++++ .../output/resgroup/resgroup_oom_1.source | 65 ++++++++++ src/test/regress/regress_gp.c | 113 ++++++++++++++++++ 12 files changed, 435 insertions(+), 19 deletions(-) create mode 100644 src/test/isolation2/input/resgroup/resgroup_oom.source create mode 100644 src/test/isolation2/output/resgroup/resgroup_oom.source create mode 100644 src/test/isolation2/output/resgroup/resgroup_oom_1.source diff --git a/src/backend/cdb/dispatcher/cdbdisp_async.c b/src/backend/cdb/dispatcher/cdbdisp_async.c index c41dbe55a72d..279b50cc7e34 100644 --- a/src/backend/cdb/dispatcher/cdbdisp_async.c +++ b/src/backend/cdb/dispatcher/cdbdisp_async.c @@ -685,6 +685,8 @@ checkDispatchResult(CdbDispatcherState *ds, int timeout_sec) handlePollSuccess(pParms, fds); } + SIMPLE_FAULT_INJECTOR("check_dispatch_result_end"); + pfree(fds); } diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 9979a60b9e7a..5a175ab64a2a 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -109,13 +109,31 @@ int getpeereid(int, uid_t *__restrict__, gid_t *__restrict__); * than looking into errcodes.h since it reflects historical behavior * rather than that of the current code. */ +#ifdef ERRCODE_APPNAME_UNKNOWN +#undef ERRCODE_APPNAME_UNKNOWN +#endif + #define ERRCODE_APPNAME_UNKNOWN "42704" /* This is part of the protocol so just define it */ +#ifdef ERRCODE_INVALID_PASSWORD +#undef ERRCODE_INVALID_PASSWORD +#endif + #define ERRCODE_INVALID_PASSWORD "28P01" + /* This too */ +#ifdef ERRCODE_CANNOT_CONNECT_NOW +#undef ERRCODE_CANNOT_CONNECT_NOW +#endif + #define ERRCODE_CANNOT_CONNECT_NOW "57P03" + /* And this GPDB-specific one, too */ +#ifdef ERRCODE_MIRROR_READY +#undef ERRCODE_MIRROR_READY +#endif + #define ERRCODE_MIRROR_READY "57M02" /* diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index f13c18bad621..bf4f7871e96a 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -151,7 +151,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) { PGresult *result; - result = (PGresult *) malloc(sizeof(PGresult)); + result = (PGresult *) pqPalloc(sizeof(PGresult)); if (!result) return NULL; @@ -407,7 +407,7 @@ dupEvents(PGEvent *events, int count) if (!events || count <= 0) return NULL; - newEvents = (PGEvent *) malloc(count * sizeof(PGEvent)); + newEvents = (PGEvent *) pqPalloc(count * sizeof(PGEvent)); if (!newEvents) return NULL; @@ -417,12 +417,12 @@ dupEvents(PGEvent *events, int count) newEvents[i].passThrough = events[i].passThrough; newEvents[i].data = NULL; newEvents[i].resultInitialized = FALSE; - newEvents[i].name = strdup(events[i].name); + newEvents[i].name = pqPstrdup(events[i].name); if (!newEvents[i].name) { while (--i >= 0) - free(newEvents[i].name); - free(newEvents); + pqPfree(newEvents[i].name); + pqPfree(newEvents); return NULL; } } @@ -585,7 +585,7 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary) */ if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD) { - block = (PGresult_data *) malloc(nBytes + PGRESULT_BLOCK_OVERHEAD); + block = (PGresult_data *) pqPalloc(nBytes + PGRESULT_BLOCK_OVERHEAD); if (!block) return NULL; space = block->space + PGRESULT_BLOCK_OVERHEAD; @@ -609,7 +609,7 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary) } /* Otherwise, start a new block. */ - block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE); + block = (PGresult_data *) pqPalloc(PGRESULT_DATA_BLOCKSIZE); if (!block) return NULL; block->next = res->curBlock; @@ -709,18 +709,18 @@ PQclear(PGresult *res) } if (res->events) - free(res->events); + pqPfree(res->events); /* Free all the subsidiary blocks */ while ((block = res->curBlock) != NULL) { res->curBlock = block->next; - free(block); + pqPfree(block); } /* Free the top-level tuple pointer array */ if (res->tuples) - free(res->tuples); + pqPfree(res->tuples); /* zero out the pointer fields to catch programming errors */ res->attDescs = NULL; @@ -732,20 +732,20 @@ PQclear(PGresult *res) /* res->curBlock was zeroed out earlier */ if (res->extras) - free(res->extras); + pqPfree(res->extras); res->extraslen = 0; res->extras = NULL; if (res->aotupcounts) - free(res->aotupcounts); + pqPfree(res->aotupcounts); res->naotupcounts = 0; if (res->waitGxids) - free(res->waitGxids); + pqPfree(res->waitGxids); res->waitGxids = NULL; res->nWaits = 0; /* Free the PGresult structure itself */ - free(res); + pqPfree(res); } /* @@ -951,10 +951,10 @@ pqAddTuple(PGresult *res, PGresAttValue *tup, const char **errmsgp) if (res->tuples == NULL) newTuples = (PGresAttValue **) - malloc(newSize * sizeof(PGresAttValue *)); + pqPalloc(newSize * sizeof(PGresAttValue *)); else newTuples = (PGresAttValue **) - realloc(res->tuples, newSize * sizeof(PGresAttValue *)); + pqRepalloc(res->tuples, newSize * sizeof(PGresAttValue *)); if (!newTuples) return FALSE; /* malloc or realloc failed */ res->tupArrSize = newSize; diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index c311a7ccbd31..25a0c731c168 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -267,7 +267,7 @@ pqParseInput3(PGconn *conn) /* now just loop through */ conn->result->aotupcounts = - malloc(sizeof(PQaoRelTupCount) * conn->result->naotupcounts); + pqPalloc(sizeof(PQaoRelTupCount) * conn->result->naotupcounts); ao = conn->result->aotupcounts; for (i = 0; i < conn->result->naotupcounts; i++) { @@ -529,7 +529,7 @@ pqParseInput3(PGconn *conn) if (pqGetInt(&conn->result->extraslen, 4, conn)) return; - conn->result->extras = malloc(conn->result->extraslen); + conn->result->extras = pqPalloc(conn->result->extraslen); if (pqGetnchar((char *)conn->result->extras, conn->result->extraslen, conn)) return; conn->asyncStatus = PGASYNC_READY; @@ -555,7 +555,7 @@ pqParseInput3(PGconn *conn) { if (conn->result->waitGxids == NULL) conn->result->waitGxids = - malloc(sizeof(int) * conn->result->nWaits); + pqPalloc(sizeof(int) * conn->result->nWaits); for (i = 0; i < conn->result->nWaits; i++) { int gxid; diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 8f17aa323721..cb0c11f534ea 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -83,6 +83,34 @@ typedef struct #endif #endif /* USE_SSL */ +/* + * Definitions meant to lessen the malloc() usage for CDB routines in + * server-sided code for struct PGresult allocations. + */ +#ifndef FRONTEND +#include "postgres.h" +#include "utils/memutils.h" + +/* + * TopTransactionContext's lifetime lasts until the end of the current query, + * which does the job well for backends. Auxiliary processes do not set + * transaction contexts, so we have to use TopMemoryContext to achieve a + * lifetime equivalent to malloc(). + */ +#define PQ_PALLOC_CONTEXT \ + ((TopTransactionContext != NULL) ? TopTransactionContext : TopMemoryContext) + +#define pqPalloc(sz) MemoryContextAlloc(PQ_PALLOC_CONTEXT, sz) +#define pqPstrdup(x) MemoryContextStrdup(PQ_PALLOC_CONTEXT, x) +#define pqRepalloc(x, sz) repalloc(x, sz) +#define pqPfree(x) pfree(x) +#else +#define pqPalloc(sz) malloc(sz) +#define pqPstrdup(x) strdup(x) +#define pqRepalloc(x, sz) realloc(x, sz) +#define pqPfree(x) free(x) +#endif + /* * POSTGRES backend dependent Constants. */ diff --git a/src/test/isolation2/input/parallel_retrieve_cursor/fault_inject.source b/src/test/isolation2/input/parallel_retrieve_cursor/fault_inject.source index 4e222432f611..355d4a649b65 100644 --- a/src/test/isolation2/input/parallel_retrieve_cursor/fault_inject.source +++ b/src/test/isolation2/input/parallel_retrieve_cursor/fault_inject.source @@ -249,3 +249,28 @@ SELECT gp_inject_fault('fetch_tuples_from_endpoint', 'resume', dbid) WHERE content=2 AND role='p'; DROP TABLE t2; + +SELECT gp_inject_fault('alloc_endpoint_slot_full', 'reset', 2); +SELECT gp_inject_fault('alloc_endpoint_slot_full_reset', 'reset', 2); + +-- Test 8: QD shouldn't hang waiting. + +-- start_ignore +DROP TABLE t3; +-- end_ignore + +CREATE TABLE t3 AS SELECT generate_series(1, 10) AS a DISTRIBUTED by (a); + +SELECT gp_inject_fault('alloc_endpoint_slot_full', 'error', dbid) +FROM gp_segment_configuration WHERE role = 'p' AND content = 0; + +BEGIN; +-- QE encounters error and destroys the endpoint immediately. QD successfully +-- resets the query, sends ACKs, and discards transaction. +DECLARE c1 PARALLEL RETRIEVE CURSOR FOR SELECT * FROM t3; +ROLLBACK; + +SELECT gp_inject_fault('alloc_endpoint_slot_full', 'reset', dbid) +FROM gp_segment_configuration WHERE role = 'p' AND content = 0; + +DROP TABLE t3; diff --git a/src/test/isolation2/input/resgroup/resgroup_oom.source b/src/test/isolation2/input/resgroup/resgroup_oom.source new file mode 100644 index 000000000000..35b6d1783ffa --- /dev/null +++ b/src/test/isolation2/input/resgroup/resgroup_oom.source @@ -0,0 +1,53 @@ +-- start_matchsubs +-- m/ERROR: Out of memory.*/ +-- s/ERROR: Out of memory.*/ERROR: Out of memory/ +-- m/ERROR: Canceling query because of high VMEM usage.*/ +-- s/ERROR: Canceling query because of high VMEM usage.*/ERROR: Out of memory/ +-- end_matchsubs + +-- start_ignore +CREATE EXTENSION gp_inject_fault; +DROP FUNCTION gp_mock_cdbdispatchcommand(amount int); +DROP ROLE role_oom_test; +DROP RESOURCE GROUP rg_oom_test; +-- end_ignore +CREATE RESOURCE GROUP rg_oom_test +WITH (cpu_rate_limit=20, memory_limit=20, memory_shared_quota=100); +CREATE ROLE role_oom_test RESOURCE GROUP rg_oom_test; +CREATE FUNCTION gp_mock_cdbdispatchcommand(amount int) +RETURNS SETOF text AS '@abs_srcdir@/../regress/regress.so', +'gp_mock_cdbdispatchcommand' LANGUAGE C; + +1: SET ROLE TO role_oom_test; + +-- Freeze coordinator's session after it reads results from segments. +SELECT gp_inject_fault('check_dispatch_result_end', 'suspend', dbid) +FROM gp_segment_configuration WHERE role = 'p' AND content = -1; + +-- Send the heavy query. +1&: SELECT count(*) FROM gp_mock_cdbdispatchcommand(1000000); + +-- Wait until we receive everything. +SELECT gp_wait_until_triggered_fault('check_dispatch_result_end', 1, dbid) +FROM gp_segment_configuration WHERE role = 'p' AND content = -1; + +-- The query should've used ~135 MB of memory. Allow 15 MB error. +WITH r AS ( + SELECT (memory_usage->'-1'->'used')::text::int AS mb + FROM gp_toolkit.gp_resgroup_status WHERE rsgname = 'rg_oom_test' +) +SELECT r.mb < 150 AS "under 150 MB", r.mb > 120 AS "above 120 MB" +FROM r; + +SELECT gp_inject_fault('check_dispatch_result_end', 'reset', dbid) FROM +gp_segment_configuration WHERE role = 'p' AND content = -1; + +1<: + +-- And finally, make sure we don't enter error recursion on fail. +ALTER RESOURCE GROUP rg_oom_test SET memory_shared_quota 0; +1: SELECT count(*) FROM gp_mock_cdbdispatchcommand(10000000); + +DROP FUNCTION gp_mock_cdbdispatchcommand(amount int); +DROP ROLE role_oom_test; +DROP RESOURCE GROUP rg_oom_test; diff --git a/src/test/isolation2/isolation2_resgroup_schedule b/src/test/isolation2/isolation2_resgroup_schedule index 17e191bde5a5..142323ff5467 100644 --- a/src/test/isolation2/isolation2_resgroup_schedule +++ b/src/test/isolation2/isolation2_resgroup_schedule @@ -61,4 +61,6 @@ test: resgroup/resgroup_large_group_id test: resgroup/resgroup_startup_memory +test: resgroup/resgroup_oom + test: resgroup/disable_resgroup diff --git a/src/test/isolation2/output/parallel_retrieve_cursor/fault_inject.source b/src/test/isolation2/output/parallel_retrieve_cursor/fault_inject.source index 9a963b076992..890d75e73f61 100644 --- a/src/test/isolation2/output/parallel_retrieve_cursor/fault_inject.source +++ b/src/test/isolation2/output/parallel_retrieve_cursor/fault_inject.source @@ -770,3 +770,47 @@ ROLLBACK DROP TABLE t2; DROP + +SELECT gp_inject_fault('alloc_endpoint_slot_full', 'reset', 2); + gp_inject_fault +----------------- + Success: +(1 row) +SELECT gp_inject_fault('alloc_endpoint_slot_full_reset', 'reset', 2); + gp_inject_fault +----------------- + Success: +(1 row) + +-- Test 8: QD shouldn't hang waiting. + +-- start_ignore +DROP TABLE t3; +-- end_ignore + +CREATE TABLE t3 AS SELECT generate_series(1, 10) AS a DISTRIBUTED by (a); +CREATE 10 + +SELECT gp_inject_fault('alloc_endpoint_slot_full', 'error', dbid) FROM gp_segment_configuration WHERE role = 'p' AND content = 0; + gp_inject_fault +----------------- + Success: +(1 row) + +BEGIN; +BEGIN +-- QE encounters error and destroys the endpoint immediately. QD successfully +-- resets the query, sends ACKs, and discards transaction. +DECLARE c1 PARALLEL RETRIEVE CURSOR FOR SELECT * FROM t3; +ERROR: fault triggered, fault name:'alloc_endpoint_slot_full' fault type:'error' (seg0 127.0.0.1:6002 pid=8916) +ROLLBACK; +ROLLBACK + +SELECT gp_inject_fault('alloc_endpoint_slot_full', 'reset', dbid) FROM gp_segment_configuration WHERE role = 'p' AND content = 0; + gp_inject_fault +----------------- + Success: +(1 row) + +DROP TABLE t3; +DROP diff --git a/src/test/isolation2/output/resgroup/resgroup_oom.source b/src/test/isolation2/output/resgroup/resgroup_oom.source new file mode 100644 index 000000000000..20e0598d34d0 --- /dev/null +++ b/src/test/isolation2/output/resgroup/resgroup_oom.source @@ -0,0 +1,66 @@ +-- start_matchsubs +-- m/ERROR: Out of memory.*/ +-- s/ERROR: Out of memory.*/ERROR: Out of memory/ +-- m/ERROR: Canceling query because of high VMEM usage.*/ +-- s/ERROR: Canceling query because of high VMEM usage.*/ERROR: Out of memory/ +-- end_matchsubs + +CREATE RESOURCE GROUP rg_oom_test WITH (cpu_rate_limit=20, memory_limit=20, memory_shared_quota=100); +CREATE +CREATE ROLE role_oom_test RESOURCE GROUP rg_oom_test; +CREATE +CREATE FUNCTION gp_mock_cdbdispatchcommand(amount int) RETURNS SETOF text AS '@abs_srcdir@/../regress/regress.so', 'gp_mock_cdbdispatchcommand' LANGUAGE C; +CREATE + +1: SET ROLE TO role_oom_test; +SET + +-- Freeze coordinator's session after it reads results from segments. +SELECT gp_inject_fault('check_dispatch_result_end', 'suspend', dbid) FROM gp_segment_configuration WHERE role = 'p' AND content = -1; + gp_inject_fault +----------------- + Success: +(1 row) + +-- Send the heavy query. +1&: SELECT count(*) FROM gp_mock_cdbdispatchcommand(1000000); + +-- Wait until we receive everything. +SELECT gp_wait_until_triggered_fault('check_dispatch_result_end', 1, dbid) FROM gp_segment_configuration WHERE role = 'p' AND content = -1; + gp_wait_until_triggered_fault +------------------------------- + Success: +(1 row) + +-- The query should've used ~135 MB of memory. Allow 15 MB error. +WITH r AS ( SELECT (memory_usage->'-1'->'used')::text::int AS mb FROM gp_toolkit.gp_resgroup_status WHERE rsgname = 'rg_oom_test' ) SELECT r.mb < 150 AS "under 150 MB", r.mb > 120 AS "above 120 MB" FROM r; + under 150 MB | above 120 MB +--------------+-------------- + t | t +(1 row) + +SELECT gp_inject_fault('check_dispatch_result_end', 'reset', dbid) FROM gp_segment_configuration WHERE role = 'p' AND content = -1; + gp_inject_fault +----------------- + Success: +(1 row) + +1<: <... completed> + count +--------- + 4000000 +(1 row) + +-- And finally, make sure we don't enter error recursion on fail. +ALTER RESOURCE GROUP rg_oom_test SET memory_shared_quota 0; +ALTER +1: SELECT count(*) FROM gp_mock_cdbdispatchcommand(10000000); +ERROR: Out of memory +DETAIL: Resource group memory limit reached + +DROP FUNCTION gp_mock_cdbdispatchcommand(amount int); +DROP +DROP ROLE role_oom_test; +DROP +DROP RESOURCE GROUP rg_oom_test; +DROP diff --git a/src/test/isolation2/output/resgroup/resgroup_oom_1.source b/src/test/isolation2/output/resgroup/resgroup_oom_1.source new file mode 100644 index 000000000000..bdcb8bbc92f8 --- /dev/null +++ b/src/test/isolation2/output/resgroup/resgroup_oom_1.source @@ -0,0 +1,65 @@ +-- start_matchsubs +-- m/ERROR: Out of memory.*/ +-- s/ERROR: Out of memory.*/ERROR: Out of memory/ +-- m/ERROR: Canceling query because of high VMEM usage.*/ +-- s/ERROR: Canceling query because of high VMEM usage.*/ERROR: Out of memory/ +-- end_matchsubs + +CREATE RESOURCE GROUP rg_oom_test WITH (cpu_rate_limit=20, memory_limit=20, memory_shared_quota=100); +CREATE +CREATE ROLE role_oom_test RESOURCE GROUP rg_oom_test; +CREATE +CREATE FUNCTION gp_mock_cdbdispatchcommand(amount int) RETURNS SETOF text AS '@abs_srcdir@/../regress/regress.so', 'gp_mock_cdbdispatchcommand' LANGUAGE C; +CREATE + +1: SET ROLE TO role_oom_test; +SET + +-- Freeze coordinator's session after it reads results from segments. +SELECT gp_inject_fault('check_dispatch_result_end', 'suspend', dbid) FROM gp_segment_configuration WHERE role = 'p' AND content = -1; + gp_inject_fault +----------------- + Success: +(1 row) + +-- Send the heavy query. +1&: SELECT count(*) FROM gp_mock_cdbdispatchcommand(1000000); + +-- Wait until we receive everything. +SELECT gp_wait_until_triggered_fault('check_dispatch_result_end', 1, dbid) FROM gp_segment_configuration WHERE role = 'p' AND content = -1; + gp_wait_until_triggered_fault +------------------------------- + Success: +(1 row) + +-- The query should've used ~135 MB of memory. Allow 15 MB error. +WITH r AS ( SELECT (memory_usage->'-1'->'used')::text::int AS mb FROM gp_toolkit.gp_resgroup_status WHERE rsgname = 'rg_oom_test' ) SELECT r.mb < 150 AS "under 150 MB", r.mb > 120 AS "above 120 MB" FROM r; + under 150 MB | above 120 MB +--------------+-------------- + t | t +(1 row) + +SELECT gp_inject_fault('check_dispatch_result_end', 'reset', dbid) FROM gp_segment_configuration WHERE role = 'p' AND content = -1; + gp_inject_fault +----------------- + Success: +(1 row) + +1<: <... completed> + count +--------- + 4000000 +(1 row) + +-- And finally, make sure we don't enter error recursion on fail. +ALTER RESOURCE GROUP rg_oom_test SET memory_shared_quota 0; +ALTER +1: SELECT count(*) FROM gp_mock_cdbdispatchcommand(10000000); +ERROR: Out of memory + +DROP FUNCTION gp_mock_cdbdispatchcommand(amount int); +DROP +DROP ROLE role_oom_test; +DROP +DROP RESOURCE GROUP rg_oom_test; +DROP diff --git a/src/test/regress/regress_gp.c b/src/test/regress/regress_gp.c index 51d02238993a..8a6659060f72 100644 --- a/src/test/regress/regress_gp.c +++ b/src/test/regress/regress_gp.c @@ -2446,3 +2446,116 @@ gp_get_int_tuples(PG_FUNCTION_ARGS) SRF_RETURN_DONE(fctx); } + +typedef struct +{ + int cur_tuple_idx; + int cur_segment_idx; + int cur_segment_tuple_idx; + + int n_segments; + int n_tuples; + + Datum some_text; + struct pg_result **pg_results; +} gp_mock_cdbdispatchcommand_status; + +/* + * This test function mocks CdbDispatchCommand() with a customizable amount of + * tuples. + */ +PG_FUNCTION_INFO_V1(gp_mock_cdbdispatchcommand); +Datum +gp_mock_cdbdispatchcommand(PG_FUNCTION_ARGS) +{ + FuncCallContext *func_ctx; + gp_mock_cdbdispatchcommand_status *my_status; + + int arg_tuple_amount = PG_GETARG_INT32(0); + + if (arg_tuple_amount <= 0) + { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("gp_mock_cdbdispatchcommand() should only be " + "called with it's parameter greater than 0"))); + } + + if (SRF_IS_FIRSTCALL()) + { + func_ctx = SRF_FIRSTCALL_INIT(); + + MemoryContext oldcontext = + MemoryContextSwitchTo(func_ctx->multi_call_memory_ctx); + + my_status = palloc0(sizeof(gp_mock_cdbdispatchcommand_status)); + + /* Cache the return result. */ + my_status->some_text = CStringGetTextDatum("sometext"); + + /* Send the command to segments from QD. */ + if (Gp_role == GP_ROLE_DISPATCH) + { + char *query = + psprintf("SELECT * FROM gp_mock_cdbdispatchcommand(%d)", + arg_tuple_amount); + + CdbPgResults cdb_pgresults = {0}; + CdbDispatchCommand(query, DF_WITH_SNAPSHOT, &cdb_pgresults); + + pfree(query); + + Assert(cdb_pgresults.numResults > 0); + + for (int i = 0; i < cdb_pgresults.numResults; i++) + { + Assert(PQresultStatus(cdb_pgresults.pg_results[i]) == + PGRES_TUPLES_OK); + my_status->n_tuples += PQntuples(cdb_pgresults.pg_results[i]); + } + + my_status->n_segments = cdb_pgresults.numResults; + my_status->pg_results = cdb_pgresults.pg_results; + } + + func_ctx->user_fctx = my_status; + + MemoryContextSwitchTo(oldcontext); + } + + func_ctx = SRF_PERCALL_SETUP(); + my_status = func_ctx->user_fctx; + + /* Generate fake tuples from every segment. */ + if (my_status->cur_tuple_idx < arg_tuple_amount) + { + my_status->cur_tuple_idx++; + SRF_RETURN_NEXT(func_ctx, my_status->some_text); + } + + /* Receive tuples from the loop above on master. */ + if (Gp_role == GP_ROLE_DISPATCH) + { + while (my_status->cur_segment_idx < my_status->n_segments) + { + PGresult *res = my_status->pg_results[my_status->cur_segment_idx]; + + if (my_status->cur_segment_tuple_idx < PQntuples(res)) + { + Datum ret = CStringGetTextDatum( + PQgetvalue(res, my_status->cur_segment_tuple_idx, 0)); + + my_status->cur_segment_tuple_idx++; + SRF_RETURN_NEXT(func_ctx, ret); + } + + PQclear(res); + + my_status->cur_segment_idx++; + my_status->cur_segment_tuple_idx = 0; + } + + pfree(my_status->pg_results); + } + + SRF_RETURN_DONE(func_ctx); +} From 67dca3da3ebdd828ccb25a4818a5b908cc08f45c Mon Sep 17 00:00:00 2001 From: Alexander Kondakov Date: Wed, 15 Oct 2025 11:36:36 +0300 Subject: [PATCH 017/111] Accept vacuum operations in XactLockTableWait assertion (#86) Function XactLockTableWait() calls LocalXidGetDistributedXid() which may get gxid corresponding to local wait xid from distributed clog in case if the dtx (which we are waiting for) managed to commit by that time we access its gxid. And for such case there is an assertion introduced by commit 13a1f66. The assert indicates that the commited transaction was just running in parallel with current one, meaning there is no other reason to access distributed transaction history. If the transaction was commited long time ago the XactLockTableWait() would never be called. However, there is a case when we can't compare the timestamps: vacuum operation, which performs in-place update of pg_database (or pg_class) without being in distributed transaction. For this case this patch extends the assertion by allowing current timestamp to have zero value. The new test related to this case is added to file 2c2753a. --- src/backend/storage/ipc/procarray.c | 2 +- .../expected/intra-grant-inplace-db.out | 60 +++++++++++++++++++ .../isolation2/sql/intra-grant-inplace-db.sql | 34 +++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 4962759ad21d..5fe261bdfe9e 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -4880,7 +4880,7 @@ LocalXidGetDistributedXid(TransactionId xid) { DistributedLog_GetDistributedXid(xid, &tstamp, &gxid); AssertImply(gxid != InvalidDistributedTransactionId, - tstamp == MyTmGxact->distribTimeStamp); + (tstamp == MyTmGxact->distribTimeStamp || MyTmGxact->distribTimeStamp == 0)); } return gxid; diff --git a/src/test/isolation2/expected/intra-grant-inplace-db.out b/src/test/isolation2/expected/intra-grant-inplace-db.out index e21a5c7cef94..7b8a39d9f235 100644 --- a/src/test/isolation2/expected/intra-grant-inplace-db.out +++ b/src/test/isolation2/expected/intra-grant-inplace-db.out @@ -46,3 +46,63 @@ DROP 1q: ... 2q: ... 3q: ... + +-- Same test as the above, except the GRANT transaction commits before the +-- second transaction check the wait gxid, it should get the gxid from +-- pg_distributedlog instead of the procarray. +CREATE ROLE regress_temp_grantee; +CREATE + +3: CREATE TEMPORARY TABLE frozen_witness (x xid) distributed by (x); +CREATE +-- observe datfrozenxid +3: INSERT INTO frozen_witness SELECT datfrozenxid FROM pg_database WHERE datname = current_catalog; +INSERT 1 +1: BEGIN; +BEGIN +-- heap_update(pg_database) +1: GRANT TEMP ON DATABASE isolation2test TO regress_temp_grantee; +GRANT +-- suspend before get 'wait gxid' +2: SELECT gp_inject_fault('before_get_distributed_xid', 'suspend', dbid) FROM gp_segment_configuration WHERE role='p' AND content=0; + gp_inject_fault +----------------- + Success: +(1 row) +-- inplace update +2&: VACUUM (FREEZE); +3: SELECT gp_wait_until_triggered_fault('before_get_distributed_xid', 1, dbid) FROM gp_segment_configuration WHERE role='p' AND content=0; + gp_wait_until_triggered_fault +------------------------------- + Success: +(1 row) +1: COMMIT; +COMMIT +3: INSERT INTO frozen_witness SELECT datfrozenxid FROM pg_database WHERE datname = current_catalog; +INSERT 1 +3: SELECT gp_inject_fault('before_get_distributed_xid', 'reset', dbid) FROM gp_segment_configuration WHERE role='p' AND content=0; + gp_inject_fault +----------------- + Success: +(1 row) + +2<: <... completed> +VACUUM +-- Save the result in an environment variable. +-- We get the raw xid to be sure that the age in the next query will be executed on the coordinator. +3: @post_run 'TOKEN=`echo "${RAW_STR}" | awk \'NR==3\' | awk \'{print $1}\'` && echo ""' : SELECT min(x::varchar::int) FROM frozen_witness; + +-- observe datfrozenxid +3: @pre_run 'echo "${RAW_STR}" | sed "s#@TOKEN#${TOKEN}#"': SELECT 'datfrozenxid retreated' FROM pg_database WHERE datname = current_catalog AND age(datfrozenxid) > age('@TOKEN'::xid); + ?column? +---------- +(0 rows) + +REVOKE ALL ON DATABASE isolation2test FROM regress_temp_grantee; +REVOKE +DROP ROLE regress_temp_grantee; +DROP + +1q: ... +2q: ... +3q: ... diff --git a/src/test/isolation2/sql/intra-grant-inplace-db.sql b/src/test/isolation2/sql/intra-grant-inplace-db.sql index 8b9f90ac2a0d..9c80ea4378d0 100644 --- a/src/test/isolation2/sql/intra-grant-inplace-db.sql +++ b/src/test/isolation2/sql/intra-grant-inplace-db.sql @@ -31,3 +31,37 @@ DROP ROLE regress_temp_grantee; 1q: 2q: 3q: + +-- Same test as the above, except the GRANT transaction commits before the +-- second transaction check the wait gxid, it should get the gxid from +-- pg_distributedlog instead of the procarray. +CREATE ROLE regress_temp_grantee; + +3: CREATE TEMPORARY TABLE frozen_witness (x xid) distributed by (x); +-- observe datfrozenxid +3: INSERT INTO frozen_witness SELECT datfrozenxid FROM pg_database WHERE datname = current_catalog; +1: BEGIN; +-- heap_update(pg_database) +1: GRANT TEMP ON DATABASE isolation2test TO regress_temp_grantee; +-- suspend before get 'wait gxid' +2: SELECT gp_inject_fault('before_get_distributed_xid', 'suspend', dbid) FROM gp_segment_configuration WHERE role='p' AND content=0; +-- inplace update +2&: VACUUM (FREEZE); +3: SELECT gp_wait_until_triggered_fault('before_get_distributed_xid', 1, dbid) FROM gp_segment_configuration WHERE role='p' AND content=0; +1: COMMIT; +3: INSERT INTO frozen_witness SELECT datfrozenxid FROM pg_database WHERE datname = current_catalog; +3: SELECT gp_inject_fault('before_get_distributed_xid', 'reset', dbid) FROM gp_segment_configuration WHERE role='p' AND content=0; + +2<: +-- Save the result in an environment variable. +-- We get the raw xid to be sure that the age in the next query will be executed on the coordinator. +3: @post_run 'TOKEN=`echo "${RAW_STR}" | awk \'NR==3\' | awk \'{print $1}\'` && echo ""' : SELECT min(x::varchar::int) FROM frozen_witness; +-- observe datfrozenxid +3: @pre_run 'echo "${RAW_STR}" | sed "s#@TOKEN#${TOKEN}#"': SELECT 'datfrozenxid retreated' FROM pg_database WHERE datname = current_catalog AND age(datfrozenxid) > age('@TOKEN'::xid); + +REVOKE ALL ON DATABASE isolation2test FROM regress_temp_grantee; +DROP ROLE regress_temp_grantee; + +1q: +2q: +3q: From 5636bc62ab6c36a1d6389c81a5608c6f91fa90ce Mon Sep 17 00:00:00 2001 From: Vladimir Sarmin Date: Wed, 22 Oct 2025 22:57:52 +0300 Subject: [PATCH 018/111] Allow standby to start with hot_standby = on in utility mode Previously, commit 8359bfa reverted changes related to a TAP tests that required hot standby functionality since it is not available in 6.X. Standby errored out before it could fully start due to several functions that threw an error. Skip the error if connection is in utility mode. Ticket: ADBDEV-7948 --- src/backend/access/transam/twophase.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index c99a8ae10f98..5ab34980c88f 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -1283,6 +1283,9 @@ StandbyTransactionIdIsPrepared(TransactionId xid) * files, so we cannot use ReadTwoPhaseFile() here. Fortunately, this * isn't needed until we try to use Hot Standby. */ + if (Gp_role == GP_ROLE_UTILITY) + return false; + elog(ERROR, "Hot Standby not supported"); #if 0 char *buf; @@ -1883,7 +1886,8 @@ GetOldestPreparedTransaction() void StandbyRecoverPreparedTransactions(bool overwriteOK) { - elog(ERROR, "Hot Standby not supported"); + if (Gp_role != GP_ROLE_UTILITY) + elog(ERROR, "Hot Standby not supported"); } /* From e7e73617870c01e57d8d4f7b4f5dfff09857796b Mon Sep 17 00:00:00 2001 From: Vladimir Sarmin Date: Thu, 9 Oct 2025 17:14:06 +0300 Subject: [PATCH 019/111] Revert "Remove tests that depend on hot_standby" This reverts commit 8359bfa07335a262339b31344288333fbb9224ce. --- src/test/recovery/t/020_archive_status.pl | 126 +++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/src/test/recovery/t/020_archive_status.pl b/src/test/recovery/t/020_archive_status.pl index a21dbf3781ff..c2d78f7e72ce 100644 --- a/src/test/recovery/t/020_archive_status.pl +++ b/src/test/recovery/t/020_archive_status.pl @@ -5,7 +5,7 @@ use warnings; use PostgresNode; use TestLib; -use Test::More tests => 7; +use Test::More tests => 16; use Config; my $primary = get_new_node('master'); @@ -106,3 +106,127 @@ $segment_name_1, "archive success reported in pg_stat_archiver for WAL segment $segment_name_1" ); + +# Create some WAL activity and a new checkpoint so as the next standby can +# create a restartpoint. As this standby starts in crash recovery because +# of the cold backup taken previously, it needs a clean restartpoint to deal +# with existing status files. +$primary->safe_psql( + 'postgres', q{ + INSERT INTO mine SELECT generate_series(10,20) AS x; + SELECT pg_switch_xlog(); + CHECKPOINT; +}); +my $segment_name_2 = $primary->safe_psql('postgres', + q{SELECT pg_xlogfile_name(pg_current_xlog_location())}); +my $segment_path_2 = "pg_xlog/archive_status/$segment_name_2"; +my $segment_path_2_ready = "$segment_path_2.ready"; +my $segment_path_2_done = "$segment_path_2.done"; + +# Test standby with archive_mode = on. +my $standby1 = get_new_node('standby'); +$standby1->init_from_backup($primary, 'backup', has_restoring => 1); +$standby1->append_conf('postgresql.conf', q{ +archive_mode = on +wal_keep_segments = 0 +}); +my $standby1_data = $standby1->data_dir; +$standby1->start; +# First restartpoint +$standby1->safe_psql('postgres', q{CHECKPOINT}); + +# Segments are cleaned after the second restartpoint, so create a second +# one. +$primary->safe_psql( + 'postgres', q{ + INSERT INTO mine SELECT generate_series(21,30) AS x; + CHECKPOINT; + SELECT pg_switch_xlog(); +}); + +# Make sure that the standby has caught here. +my $primary_lsn = $primary->safe_psql('postgres', + q{SELECT pg_current_xlog_location()}); +$standby1->poll_query_until('postgres', + qq{ SELECT pg_xlog_location_diff(pg_last_xlog_replay_location(), '$primary_lsn') >= 0 }) + or die "Timed out while waiting for xlog replay"; + +# Second restartpoint. +$standby1->safe_psql('postgres', q{CHECKPOINT}); + +# Recovery with archive_mode=on removed .ready signal files inherited +# from backup after two checkpoints. Note that this WAL segment +# existed in the backup. +ok( !-f "$standby1_data/$segment_path_1_ready", + ".ready file for WAL segment $segment_name_1 present in backup removed with archive_mode=on on standby" +); + +# Recovery with archive_mode=on should not create .ready files. +# Note that this segment did not exist in the backup. +ok( !-f "$standby1_data/$segment_path_2_ready", + ".ready file for WAL segment $segment_name_2 not created on standby when archive_mode=on on standby" +); + +# Recovery with archive_mode = on creates .done files. +ok( -f "$standby1_data/$segment_path_2_done", + ".done file for WAL segment $segment_name_2 created when archive_mode=on on standby" +); + +# Test recovery with archive_mode = always, which should always keep +# .ready files if archiving is enabled, though here we want the archive +# command to fail to persist the .ready files. Note that this node +# has inherited the archive command of the previous cold backup that +# will cause archiving failures. +my $standby2 = get_new_node('standby2'); +$standby2->init_from_backup($primary, 'backup', has_restoring => 1); +$standby2->append_conf('postgresql.conf', 'archive_mode = always'); +my $standby2_data = $standby2->data_dir; +$standby2->start; + +$standby2->safe_psql('postgres', q{CHECKPOINT}); + +ok( -f "$standby2_data/$segment_path_1_ready", + ".ready file for WAL segment $segment_name_1 existing in backup is kept with archive_mode=always on standby" +); + +ok( -f "$standby2_data/$segment_path_2_ready", + ".ready file for WAL segment $segment_name_2 created with archive_mode=always on standby" +); + +# Reset statistics of the archiver for the next checks. +$standby2->safe_psql('postgres', q{SELECT pg_stat_reset_shared('archiver')}); + +# Now crash the cluster to check that recovery step does not +# remove non-archived WAL segments on a standby where archiving +# is enabled. +$standby2->stop('immediate'); +$standby2->start; + +ok( -f "$standby2_data/$segment_path_1_ready", + "WAL segment still ready to archive after crash recovery on standby with archive_mode=always" +); + +# Allow WAL archiving again, and wait for the segments to be archived. +$standby2->safe_psql( + 'postgres', q{ + ALTER SYSTEM RESET archive_command; + SELECT pg_reload_conf(); +}); +$standby2->poll_query_until('postgres', + qq{SELECT last_archived_wal = '$segment_name_2' FROM pg_stat_archiver}) + or die "Timed out while waiting for archiving to finish"; + +is( $standby2->safe_psql( + 'postgres', q{SELECT archived_count FROM pg_stat_archiver}), + '3', + 'correct number of WAL segments archived from standby'); + +ok( !-f "$standby2_data/$segment_path_1_ready" + && !-f "$standby2_data/$segment_path_2_ready", + ".ready files removed after archive success with archive_mode=always on standby" +); + +ok( -f "$standby2_data/$segment_path_1_done" + && -f "$standby2_data/$segment_path_2_done", + ".done files created after archive success with archive_mode=always on standby" +); From 8b638b777fc5e9968711750693a87844cf07af2b Mon Sep 17 00:00:00 2001 From: Roman Eskin Date: Tue, 30 Sep 2025 14:02:28 +1000 Subject: [PATCH 020/111] Fix the 'max_tm_gxacts' value after fault injection (#106) Problem description: After sequential execution of isolation2 tests 'standby_replay_dtx_info' and 'ao_unique_index' the coordinator's standby postmaster process together with its children processes were terminated. Root cause: Test 'standby_replay_dtx_info' sets fault injection 'standby_gxacts_overflow' on coordinator's standby, which updates the global var 'max_tm_gxacts' (the limit of distributed transactions) to 1, but at the reset of this fault the value of 'max_tm_gxacts' was not updated to its original value. Therefore, on any next test that created more than 2 distributed transactions that were replayed on the standby, the standby encountered the fatal error "the limit of 1 distributed transactions has been reached" and it was terminated. Fix: Set 'max_tm_gxacts' to its original value when fault injection 'standby_gxacts_overflow' is not set. (cherry picked from commit 423cc57b779bfb8f048f47425b428091a7d959a9) --- src/backend/cdb/cdbdtxrecovery.c | 6 ++++ .../expected/standby_replay_dtx_info.out | 36 +++++++++++++++++++ .../sql/standby_replay_dtx_info.sql | 22 ++++++++++++ 3 files changed, 64 insertions(+) diff --git a/src/backend/cdb/cdbdtxrecovery.c b/src/backend/cdb/cdbdtxrecovery.c index ec7ea21099da..cd83c41e84ec 100644 --- a/src/backend/cdb/cdbdtxrecovery.c +++ b/src/backend/cdb/cdbdtxrecovery.c @@ -643,11 +643,17 @@ redoDistributedCommitRecord(TMGXACT_LOG *gxact_log) if (i == *shmNumCommittedGxacts) { #ifdef FAULT_INJECTOR + static int save_max_tm_gxacts = -1; + if (save_max_tm_gxacts < 0) + save_max_tm_gxacts = max_tm_gxacts; + if (SIMPLE_FAULT_INJECTOR("standby_gxacts_overflow") == FaultInjectorTypeSkip) { max_tm_gxacts = 1; elog(LOG, "Committed gid array length: %d", *shmNumCommittedGxacts); } + else + max_tm_gxacts = save_max_tm_gxacts; #endif /* diff --git a/src/test/isolation2/expected/standby_replay_dtx_info.out b/src/test/isolation2/expected/standby_replay_dtx_info.out index df3166190d2d..028077a90754 100644 --- a/src/test/isolation2/expected/standby_replay_dtx_info.out +++ b/src/test/isolation2/expected/standby_replay_dtx_info.out @@ -95,5 +95,41 @@ select gp_inject_fault_infinite('standby_gxacts_overflow', 'reset', dbid) from g (1 row) drop table test_dtx_standby_tbl; DROP + +-- Verify that max_tm_gxacts is reset from 1. In order to do so, +-- create 2 distributed transactions that are postponed before the point where +-- 'distributed forget' is inserted into the WAL, and check that standby can +-- successfully replay the WAL. +select gp_inject_fault_infinite('dtm_before_insert_forget_comitted', 'suspend', dbid) from gp_segment_configuration where role = 'p' and content = -1; + gp_inject_fault_infinite +-------------------------- + Success: +(1 row) + +1&: create table test1(a int) distributed by (a); +2&: create table test2(a int) distributed by (a); + +-- Ensure that replay on standby is ok. +select wait_for_standby_replay(1200); + wait_for_standby_replay +------------------------- + t +(1 row) + +select gp_inject_fault_infinite('dtm_before_insert_forget_comitted', 'reset', dbid) from gp_segment_configuration where role = 'p' and content = -1; + gp_inject_fault_infinite +-------------------------- + Success: +(1 row) + +1<: <... completed> +CREATE +2<: <... completed> +CREATE + +drop table test1; +DROP +drop table test2; +DROP drop function wait_for_standby_replay(int); DROP diff --git a/src/test/isolation2/sql/standby_replay_dtx_info.sql b/src/test/isolation2/sql/standby_replay_dtx_info.sql index d4757a498eb9..0dbce5b81db4 100644 --- a/src/test/isolation2/sql/standby_replay_dtx_info.sql +++ b/src/test/isolation2/sql/standby_replay_dtx_info.sql @@ -72,4 +72,26 @@ select wait_for_standby_replay(1200); select gp_inject_fault_infinite('standby_gxacts_overflow', 'reset', dbid) from gp_segment_configuration where content = -1 and role = 'm'; drop table test_dtx_standby_tbl; + +-- Verify that max_tm_gxacts is reset from 1. In order to do so, +-- create 2 distributed transactions that are postponed before the point where +-- 'distributed forget' is inserted into the WAL, and check that standby can +-- successfully replay the WAL. +select gp_inject_fault_infinite('dtm_before_insert_forget_comitted', 'suspend', dbid) +from gp_segment_configuration where role = 'p' and content = -1; + +1&: create table test1(a int) distributed by (a); +2&: create table test2(a int) distributed by (a); + +-- Ensure that replay on standby is ok. +select wait_for_standby_replay(1200); + +select gp_inject_fault_infinite('dtm_before_insert_forget_comitted', 'reset', dbid) +from gp_segment_configuration where role = 'p' and content = -1; + +1<: +2<: + +drop table test1; +drop table test2; drop function wait_for_standby_replay(int); From 1b63b1ba5480c5a1af4c6499c2ce7771c1ed1dbd Mon Sep 17 00:00:00 2001 From: Georgy Shelkovy Date: Fri, 31 Oct 2025 12:55:53 +0500 Subject: [PATCH 021/111] Don't create motion on QE slice (#111) The planner can execute some functions on segments. However, their contents will also be planned on segments. Planning may create motions, which is unacceptable on segments. Since the contents of functions may be unavailable when planning the initial query (for example, a C-function with a call to SQL in the SPI), it is sufficient to prevent motions from being created when planning a function on a segment. Ticket: ADBDEV-8689 (cherry picked from commit 50385e2ebc768a92cda0692a604df80981061512) --- src/backend/optimizer/plan/createplan.c | 7 +++++++ .../expected/qp_functions_in_select.out | 12 ++++++++++++ .../regress/sql/qp_functions_in_select.sql | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index dba836fe40fb..e8edcc66aa8d 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -5702,6 +5702,13 @@ make_motion(PlannerInfo *root, Plan *lefttree, Oid *sortOperators, Oid *collations, bool *nullsFirst, bool useExecutorVarFormat) { + if (Gp_role != GP_ROLE_DISPATCH) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot create Motion on QE slice"))); + } + Motion *node = makeNode(Motion); Plan *plan = &node->plan; diff --git a/src/test/regress/expected/qp_functions_in_select.out b/src/test/regress/expected/qp_functions_in_select.out index 98595a51ed96..ee479f454b00 100644 --- a/src/test/regress/expected/qp_functions_in_select.out +++ b/src/test/regress/expected/qp_functions_in_select.out @@ -7673,3 +7673,15 @@ ERROR: UPDATE is not allowed in a non-volatile function CONTEXT: SQL statement "UPDATE bar SET d = d+1 WHERE c = $1" PL/pgSQL function func2_mod_int_stb(integer) line 3 at SQL statement rollback; +-- Test motions are not allowed on QE +create table d(i int) distributed by (i); +insert into d select 1; +create function f() returns int as $$ + select 1 from gp_dist_random('pg_group') union all select 1 from pg_group; +$$ language sql; +-- error when execute such function on QE +select f() from d; +ERROR: cannot create Motion on QE slice (seg1 slice1 172.18.0.7:6003 pid=37574) +CONTEXT: SQL function "f" during startup +drop table d; +drop function f(); diff --git a/src/test/regress/sql/qp_functions_in_select.sql b/src/test/regress/sql/qp_functions_in_select.sql index 19e657d06b9e..5c1af4581f24 100644 --- a/src/test/regress/sql/qp_functions_in_select.sql +++ b/src/test/regress/sql/qp_functions_in_select.sql @@ -2111,3 +2111,21 @@ rollback; begin; SELECT func1_mod_setint_stb(func2_mod_int_stb(5)) order by 1; rollback; + +-- Test motions are not allowed on QE +--start_ignore +drop table if exists d; +drop function if exists f(); +--end_ignore +create table d(i int) distributed by (i); +insert into d select 1; + +create function f() returns int as $$ + select 1 from gp_dist_random('pg_group') union all select 1 from pg_group; +$$ language sql; + +-- error when execute such function on QE +select f() from d; + +drop table d; +drop function f(); From 1d42b1f9a119310c60da8eb22cbf68808a149602 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Thu, 13 Nov 2025 19:37:58 +0700 Subject: [PATCH 022/111] ADBDEV-8787: (v6) Run tests for feature branches with slashes ## ADBDEV-8787: (v6) Run tests for feature branches with slashes (#115) - Update pull-request branches to '**' --- .github/workflows/greengage-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index ebc1213dfa16..bf686e0b1dad 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -6,7 +6,7 @@ on: branches: ['main'] # Trigger on push to main (after merged PR) tags: ['6.*'] # Trigger on tags for versioned releases pull_request: - branches: ['*'] # Trigger on pull requests for all branches + branches: ['**'] # Trigger on pull requests for all branches # Concurrency control to cancel previous runs on new push to same PR/branch concurrency: From f99696ff344f96dbe73226cb0e5c6cb495a98538 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Wed, 19 Nov 2025 13:29:14 +0700 Subject: [PATCH 023/111] The pipeline build is not working Target CI jobs to v8 - build - behave-tests - regression-tests - orca-tests - upload Ticket: ADBDEV-8833 --- .github/workflows/greengage-ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index bf686e0b1dad..85127375d712 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -23,7 +23,7 @@ jobs: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-build.yml@v3 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-build.yml@v8 with: version: 6 target_os: ${{ matrix.target_os }} @@ -41,7 +41,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v1 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v8 with: version: 6 target_os: ${{ matrix.target_os }} @@ -59,7 +59,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-regression.yml@v1 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-regression.yml@v8 with: version: 6 target_os: ${{ matrix.target_os }} @@ -77,7 +77,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-orca.yml@v1 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-orca.yml@v8 with: version: 6 target_os: ${{ matrix.target_os }} @@ -113,7 +113,7 @@ jobs: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-upload.yml@v1 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-upload.yml@v8 with: version: 6 target_os: ${{ matrix.target_os }} From 5f699fc3969a22dc95a0a829dadcb41b34c786b6 Mon Sep 17 00:00:00 2001 From: Divyesh Vanjare Date: Tue, 24 Jan 2023 11:18:15 -0500 Subject: [PATCH 024/111] Fix memory corruption for AOCO addcol abort If the add column + insert gets aborted, pg_aocsseg still holds the aborted column's vpinfo. We need to read only the committed columns' vpinfo and ignore all aborted column's vpinfo. Before this change during pg_aocsseg reads we were copying over the whole vpinfo which includes entries for aborted columns This creates memory corruption as we are copying over more than what is needed/commited (aborted column's vpinfo) This change edits the read of vpinfo to limit upto committed columns. All the other code paths (that assert for VARSIZE(dv) == aocs_vpinfo_size) don't encounter failure with aborted columns, as they are only reached after aocsseg is already updated in same transaction. AOCSFileSegInfoAddVpe doesn't encounter this problem as we always update aocsseg entry with empty vpe (with new number of columns) in aocs_addcol_emptyvpe before we reach AOCSFileSegInfoAddVpe in aocs_addcol_closefiles (cherry picked from commit 0a2d3cb7cfe35010afc769cdb970df7d5bb0e0a1) Changes compared to the original commit: 1. 6x specific change in the test query: changed 'USING ao_column' to 'WITH (APPENDONLY=TRUE, ORIENTATION=COLUMN)'. 2. 6x specific change in the test query: in the queries with 'gp_toolkit.__gp_aocsseg()' removed ordering by segment_id, as it doesn't exist. 3. Updated test answer file according to the changes above. --- src/backend/access/aocs/aocssegfiles.c | 110 ++++++++++++------ src/include/access/aocssegfiles.h | 1 - .../regress/expected/alter_table_aocs.out | 65 +++++++++++ src/test/regress/sql/alter_table_aocs.sql | 15 +++ 4 files changed, 152 insertions(+), 39 deletions(-) diff --git a/src/backend/access/aocs/aocssegfiles.c b/src/backend/access/aocs/aocssegfiles.c index f93e380d248f..04d890e77f6e 100644 --- a/src/backend/access/aocs/aocssegfiles.c +++ b/src/backend/access/aocs/aocssegfiles.c @@ -61,8 +61,7 @@ static AOCSFileSegInfo **GetAllAOCSFileSegInfo_pg_aocsseg_rel( - int numOfColumsn, - char *relationName, + Relation rel, Relation pg_aocsseg_rel, Snapshot appendOnlyMetaDataSnapshot, int32 *totalseg); @@ -127,6 +126,68 @@ InsertInitialAOCSFileSegInfo(Relation prel, int32 segno, int32 nvp) pfree(values); } +/* + * This is a routine to extract the vpinfo underlying the untoasted datum from + * the pg_aocsseg relation row, given the aocs relation's relnatts, into the supplied + * AOCSFileSegInfo structure. + * + * Sometimes the number of columns represented in the vpinfo inside pg_aocsseg + * the row may not match pg_class.relnatts. For instance, when we do an ADD + * COLUMN operation, we will have lesser number of columns in the table row + * than pg_class.relnatts. + * On the other hand, following an aborted ADD COLUMN operation, + * the number of columns in the table row will be + * greater than pg_class.relnatts. + */ +static void +deformAOCSVPInfo(Relation rel, struct varlena *v, AOCSFileSegInfo *aocs_seginfo) +{ + int16 relnatts = RelationGetNumberOfAttributes(rel); + struct varlena *dv = pg_detoast_datum(v); + int source_size = VARSIZE(dv); + int target_size = aocs_vpinfo_size(relnatts); + + + if (source_size <= target_size) + { + /* The source fits into the target, simply memcpy. */ + memcpy(&aocs_seginfo->vpinfo, dv, source_size); + Assert(aocs_seginfo->vpinfo.nEntry <= relnatts); + } + else + { + /* + * We have more columns represented in the vpinfo recorded inside the + * pg_aocsseg row, than pg_class.relnatts. Perform additional validation + * on these extra column entries and then simply copy over relnatts + * worth of entries from within the datum. + */ + AOCSVPInfo *vpInfo = (AOCSVPInfo *) dv; + + for (int i = relnatts; i < vpInfo->nEntry; ++i) + { + /* + * These extra entries must have be the initial frozen inserts + * from when InsertInitialAOCSFileSegInfo() was called during + * an aborted ADD COLUMN operation. Such entries should have eofs = 0, + * indicating that there is no data. If not, there is something + * seriously wrong. Yell appropriately. + */ + if(vpInfo->entry[i].eof > 0 || vpInfo->entry[i].eof_uncompressed > 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("For relation \"%s\" aborted column %d has non-zero eof %d or non-zero uncompressed eof %d", + RelationGetRelationName(rel), i, (int) vpInfo->entry[i].eof, (int) vpInfo->entry[i].eof_uncompressed))); + } + + memcpy(&aocs_seginfo->vpinfo, dv, aocs_vpinfo_size(relnatts)); + aocs_seginfo->vpinfo.nEntry = relnatts; + } + + if (dv != v) + pfree(dv); +} + /* * GetAOCSFileSegInfo. * @@ -223,15 +284,9 @@ GetAOCSFileSegInfo(Relation prel, seginfo ->state = DatumGetInt16(d[Anum_pg_aocs_state - 1]); Assert(!null[Anum_pg_aocs_vpinfo - 1]); - { - struct varlena *v = (struct varlena *) DatumGetPointer(d[Anum_pg_aocs_vpinfo - 1]); - struct varlena *dv = pg_detoast_datum(v); - - Assert(VARSIZE(dv) <= aocs_vpinfo_size(nvp)); - memcpy(&seginfo->vpinfo, dv, aocs_vpinfo_size(nvp)); - if (dv != v) - pfree(dv); - } + deformAOCSVPInfo(prel, + (struct varlena *) DatumGetPointer(d[Anum_pg_aocs_vpinfo - 1]), + seginfo); pfree(d); pfree(null); @@ -255,9 +310,7 @@ GetAllAOCSFileSegInfo(Relation prel, pg_aocsseg_rel = relation_open(prel->rd_appendonly->segrelid, AccessShareLock); - results = GetAllAOCSFileSegInfo_pg_aocsseg_rel( - RelationGetNumberOfAttributes(prel), - RelationGetRelationName(prel), + results = GetAllAOCSFileSegInfo_pg_aocsseg_rel(prel, pg_aocsseg_rel, appendOnlyMetaDataSnapshot, totalseg); @@ -285,17 +338,13 @@ aocsFileSegInfoCmp(const void *left, const void *right) return 0; } - static AOCSFileSegInfo ** -GetAllAOCSFileSegInfo_pg_aocsseg_rel(int numOfColumns, - char *relationName, +GetAllAOCSFileSegInfo_pg_aocsseg_rel(Relation rel, Relation pg_aocsseg_rel, Snapshot snapshot, int32 *totalseg) { - int32 nvp = numOfColumns; - HeapScanDesc scan; HeapTuple tup; @@ -327,7 +376,7 @@ GetAllAOCSFileSegInfo_pg_aocsseg_rel(int numOfColumns, allseg = (AOCSFileSegInfo **) repalloc(allseg, sizeof(AOCSFileSegInfo *) * seginfo_slot_no); } - aocs_seginfo = (AOCSFileSegInfo *) palloc0(aocsfileseginfo_size(nvp)); + aocs_seginfo = (AOCSFileSegInfo *) palloc0(aocsfileseginfo_size(RelationGetNumberOfAttributes(rel))); allseg[cur_seg] = aocs_seginfo; @@ -364,20 +413,7 @@ GetAllAOCSFileSegInfo_pg_aocsseg_rel(int numOfColumns, aocs_seginfo->state = DatumGetInt16(d[Anum_pg_aocs_state - 1]); Assert(!null[Anum_pg_aocs_vpinfo - 1]); - { - struct varlena *v = (struct varlena *) DatumGetPointer(d[Anum_pg_aocs_vpinfo - 1]); - struct varlena *dv = pg_detoast_datum(v); - - /* - * VARSIZE(dv) may be less than aocs_vpinfo_size, in case of add - * column, we try to do a ctas from old table to new table. - */ - Assert(VARSIZE(dv) <= aocs_vpinfo_size(nvp)); - - memcpy(&aocs_seginfo->vpinfo, dv, VARSIZE(dv)); - if (dv != v) - pfree(dv); - } + deformAOCSVPInfo(rel, (struct varlena *) DatumGetPointer(d[Anum_pg_aocs_vpinfo - 1]), aocs_seginfo); ++cur_seg; } @@ -1334,8 +1370,7 @@ gp_aocsseg_internal(PG_FUNCTION_ARGS, Oid aocsRelOid) pg_aocsseg_rel = heap_open(aocsRel->rd_appendonly->segrelid, AccessShareLock); context->aocsSegfileArray = GetAllAOCSFileSegInfo_pg_aocsseg_rel( - aocsRel->rd_rel->relnatts, - RelationGetRelationName(aocsRel), + aocsRel, pg_aocsseg_rel, appendOnlyMetaDataSnapshot, &context->totalAocsSegFiles); @@ -1544,8 +1579,7 @@ gp_aocsseg_history(PG_FUNCTION_ARGS) pg_aocsseg_rel = heap_open(aocsRel->rd_appendonly->segrelid, AccessShareLock); context->aocsSegfileArray = GetAllAOCSFileSegInfo_pg_aocsseg_rel( - RelationGetNumberOfAttributes(aocsRel), - RelationGetRelationName(aocsRel), + aocsRel, pg_aocsseg_rel, SnapshotAny, //Get ALL tuples from pg_aocsseg_ % including aborted and in - progress ones. & context->totalAocsSegFiles); diff --git a/src/include/access/aocssegfiles.h b/src/include/access/aocssegfiles.h index 99937ebbae24..2032f5fcdb83 100644 --- a/src/include/access/aocssegfiles.h +++ b/src/include/access/aocssegfiles.h @@ -137,7 +137,6 @@ extern AOCSFileSegInfo *GetAOCSFileSegInfo(Relation prel, Snapshot appendOnlyMetaDataSnapshot, int32 segno); - extern AOCSFileSegInfo **GetAllAOCSFileSegInfo(Relation prel, Snapshot appendOnlyMetaDataSnapshot, int *totalseg); diff --git a/src/test/regress/expected/alter_table_aocs.out b/src/test/regress/expected/alter_table_aocs.out index 5a118b0a0806..d1f2400d6ce5 100644 --- a/src/test/regress/expected/alter_table_aocs.out +++ b/src/test/regress/expected/alter_table_aocs.out @@ -895,3 +895,68 @@ SELECT * FROM gp_toolkit.__gp_aocsseg('aoco_insert_empty_row'::regclass); --------+-------+------------+----------------+----------+-----+------------------+----------+---------------+------- (0 rows) +-- test case: Ensure that reads don't fail after aborting an add column + insert operation and we don't project the aborted column +CREATE TABLE aocs_addcol_abort(a int, b int) WITH (APPENDONLY=TRUE, ORIENTATION=COLUMN); +INSERT INTO aocs_addcol_abort SELECT i,i FROM generate_series(1,10)i; +BEGIN; +ALTER TABLE aocs_addcol_abort ADD COLUMN c int; +INSERT INTO aocs_addcol_abort SELECT i,i,i FROM generate_series(1,10)i; +-- check state of aocsseg for entries of add column + insert +SELECT * FROM gp_toolkit.__gp_aocsseg('aocs_addcol_abort') ORDER BY column_num; + gp_tid | segno | column_num | physical_segno | tupcount | eof | eof_uncompressed | modcount | formatversion | state +--------+-------+------------+----------------+----------+-----+------------------+----------+---------------+------- + (0,4) | 1 | 0 | 1 | 20 | 0 | 0 | 3 | 3 | 1 + (0,4) | 1 | 1 | 129 | 20 | 0 | 0 | 3 | 3 | 1 + (0,4) | 1 | 2 | 257 | 20 | 0 | 0 | 3 | 3 | 1 +(3 rows) + +SELECT * FROM aocs_addcol_abort; + a | b | c +----+----+---- + 5 | 5 | 5 + 6 | 6 | 6 + 9 | 9 | 9 + 10 | 10 | 10 + 5 | 5 | + 6 | 6 | + 9 | 9 | + 10 | 10 | + 1 | 1 | 1 + 1 | 1 | + 2 | 2 | 2 + 3 | 3 | 3 + 4 | 4 | 4 + 7 | 7 | 7 + 8 | 8 | 8 + 2 | 2 | + 3 | 3 | + 4 | 4 | + 7 | 7 | + 8 | 8 | +(20 rows) + +ABORT; +-- check state of aocsseg if entries for new column are rolled back correctly +SELECT * FROM gp_toolkit.__gp_aocsseg('aocs_addcol_abort') ORDER BY column_num; + gp_tid | segno | column_num | physical_segno | tupcount | eof | eof_uncompressed | modcount | formatversion | state +--------+-------+------------+----------------+----------+-----+------------------+----------+---------------+------- + (0,2) | 1 | 0 | 1 | 10 | 0 | 0 | 1 | 3 | 1 + (0,2) | 1 | 1 | 129 | 10 | 0 | 0 | 1 | 3 | 1 +(2 rows) + +SELECT * FROM aocs_addcol_abort; + a | b +----+---- + 5 | 5 + 6 | 6 + 9 | 9 + 10 | 10 + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 7 | 7 + 8 | 8 +(10 rows) + +DROP TABLE aocs_addcol_abort; diff --git a/src/test/regress/sql/alter_table_aocs.sql b/src/test/regress/sql/alter_table_aocs.sql index 7856c4c61f27..f17a755a64e5 100644 --- a/src/test/regress/sql/alter_table_aocs.sql +++ b/src/test/regress/sql/alter_table_aocs.sql @@ -527,3 +527,18 @@ CREATE table aoco_insert_empty_row (a integer, b text, c integer) WITH (APPENDON INSERT INTO aoco_insert_empty_row SELECT 1,'a',1 FROM gp_id WHERE dbid=-999; -- assert non-empty seg entries SELECT * FROM gp_toolkit.__gp_aocsseg('aoco_insert_empty_row'::regclass); + +-- test case: Ensure that reads don't fail after aborting an add column + insert operation and we don't project the aborted column +CREATE TABLE aocs_addcol_abort(a int, b int) WITH (APPENDONLY=TRUE, ORIENTATION=COLUMN); +INSERT INTO aocs_addcol_abort SELECT i,i FROM generate_series(1,10)i; +BEGIN; +ALTER TABLE aocs_addcol_abort ADD COLUMN c int; +INSERT INTO aocs_addcol_abort SELECT i,i,i FROM generate_series(1,10)i; +-- check state of aocsseg for entries of add column + insert +SELECT * FROM gp_toolkit.__gp_aocsseg('aocs_addcol_abort') ORDER BY column_num; +SELECT * FROM aocs_addcol_abort; +ABORT; +-- check state of aocsseg if entries for new column are rolled back correctly +SELECT * FROM gp_toolkit.__gp_aocsseg('aocs_addcol_abort') ORDER BY column_num; +SELECT * FROM aocs_addcol_abort; +DROP TABLE aocs_addcol_abort; From 20c446b3393953c35c2d8a0d9dd75a0709ad08f7 Mon Sep 17 00:00:00 2001 From: Roman Eskin Date: Thu, 20 Nov 2025 11:47:11 +1000 Subject: [PATCH 025/111] Fix vacuum failure to clean up some segment files of AOCS tables Problem description: Vacuum failed to clean up segment files for AOCS tables, that were created in a transaction, which was rolled back. Root cause: For AOCS tables, a transaction that creates segment files for the very first time, inserts a corresponding record into 'pg_aoseg.pg_aocsseg_' in 'InsertInitialAOCSFileSegInfo()'. But if the transaction was rolled back, this record in 'pg_aoseg.pg_aocsseg_' was no more available. But the physical segment files still existed. Vacuum process in AOCSTruncateToEOF() relies on the information from 'pg_aoseg.pg_aocsseg_' to get all segments vacuum needs to scan. Obviously, the segment files from the aborted transaction were not visible to it. Fix: Add 'heap_freeze_tuple_wal_logged(segrel, segtup)' into 'InsertInitialAOCSFileSegInfo()', so vacuum now can see the new segment files. It is already done so in 7x (refer to 1306d47183375e3acef0bc23735b450187165d05). But this change interferes with commit 9e106f51d295fcfb37fc97383286ebeb22426402, as freezing the tuple partially reverts its logic. Part of this interference is resolved by preceding cherry-pick of 0a2d3cb7cfe35010afc769cdb970df7d5bb0e0a1, which handles the same problem as 9e106f51d295fcfb37fc97383286ebeb22426402, but in a different way. But for 6x additional changes are required in 'UpdateAOCSFileSegInfo()', so this patch adds usage of 'deformAOCSVPInfo()' into it in the manner intended by 0a2d3cb7cfe35010afc769cdb970df7d5bb0e0a1. Plus this change requires update of the output of the test 'uao_crash_compaction_column', because the output of 'gp_toolkit.__gp_aocsseg()' now contains records about segment files created by the interrupted vacuum command. --- src/backend/access/aocs/aocssegfiles.c | 19 ++++++------ .../expected/uao_crash_compaction_column.out | 5 +++- src/test/regress/expected/vacuum_full_ao.out | 21 ++++++++++++++ src/test/regress/sql/vacuum_full_ao.sql | 29 +++++++++++++++++++ 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/backend/access/aocs/aocssegfiles.c b/src/backend/access/aocs/aocssegfiles.c index 04d890e77f6e..6312528c9c53 100644 --- a/src/backend/access/aocs/aocssegfiles.c +++ b/src/backend/access/aocs/aocssegfiles.c @@ -115,6 +115,7 @@ InsertInitialAOCSFileSegInfo(Relation prel, int32 segno, int32 nvp) segtup = heap_form_tuple(RelationGetDescr(segrel), values, nulls); CatalogTupleInsert(segrel, segtup); + heap_freeze_tuple_wal_logged(segrel, segtup); heap_freetuple(segtup); heap_close(segrel, RowExclusiveLock); @@ -947,11 +948,15 @@ UpdateAOCSFileSegInfo(AOCSInsertDesc idesc) &null[Anum_pg_aocs_vpinfo - 1]); Assert(!null[Anum_pg_aocs_vpinfo - 1]); - struct varlena *v = (struct varlena *) DatumGetPointer(d_tmp); - struct varlena *dv = pg_detoast_datum(v); - Assert(VARSIZE(dv) == aocs_vpinfo_size(nvp)); - AOCSVPInfo *oldvpinfo = (AOCSVPInfo *) dv; + AOCSFileSegInfo *seginfo; + seginfo = (AOCSFileSegInfo *) palloc0(aocsfileseginfo_size(nvp)); + + deformAOCSVPInfo(prel, + (struct varlena *) DatumGetPointer(d_tmp), + seginfo); + + AOCSVPInfo *oldvpinfo = &seginfo->vpinfo; /* * Number of columns fetched from vpinfo should match number of attributes @@ -997,11 +1002,6 @@ UpdateAOCSFileSegInfo(AOCSInsertDesc idesc) } } - if (dv != v) - { - pfree(dv); - } - d[Anum_pg_aocs_vpinfo - 1] = PointerGetDatum(vpinfo); null[Anum_pg_aocs_vpinfo - 1] = false; repl[Anum_pg_aocs_vpinfo - 1] = true; @@ -1012,6 +1012,7 @@ UpdateAOCSFileSegInfo(AOCSInsertDesc idesc) pfree(newtup); pfree(vpinfo); + pfree(seginfo); heap_endscan(scan); heap_close(segrel, RowExclusiveLock); diff --git a/src/test/isolation2/expected/uao_crash_compaction_column.out b/src/test/isolation2/expected/uao_crash_compaction_column.out index 11c2ccbb3c94..047c732b7b2c 100644 --- a/src/test/isolation2/expected/uao_crash_compaction_column.out +++ b/src/test/isolation2/expected/uao_crash_compaction_column.out @@ -628,7 +628,10 @@ ERROR: Error on receive from seg0 127.0.0.1:7002 pid=29553: server closed the c 1 | 0 | 1 | 10 | 2 | 1 1 | 1 | 129 | 10 | 2 | 1 1 | 2 | 257 | 10 | 2 | 1 -(3 rows) + 2 | 0 | 2 | 0 | 0 | 1 + 2 | 1 | 130 | 0 | 0 | 1 + 2 | 2 | 258 | 0 | 0 | 1 +(6 rows) -- generates truncate xlog record for all the files having entry in -- pg_aocsseg table. 6:VACUUM crash_vacuum_in_appendonly_insert_1; diff --git a/src/test/regress/expected/vacuum_full_ao.out b/src/test/regress/expected/vacuum_full_ao.out index a79b7222d2a2..b68c24ed2ed3 100644 --- a/src/test/regress/expected/vacuum_full_ao.out +++ b/src/test/regress/expected/vacuum_full_ao.out @@ -33,3 +33,24 @@ select pg_relation_size((select segrelid from pg_appendonly where relid = 'vfao' 32768 (1 row) +-- Test that vacuum can process segment files created in an aborted transaction +create table table_ao_col (i int, j int, k int) with (appendonly='true', orientation="column"); +begin; +insert into table_ao_col select i, i + 1, i + 2 from generate_series(1, 20) i; +\! psql regression -c 'begin; insert into table_ao_col select i, i + 1, i + 2 from generate_series(1, 20) i; rollback;' +ROLLBACK +commit; +create or replace function getTableSegFiles(t regclass, out gp_contentid smallint, out filepath text) +as 'select current_setting(''gp_contentid'')::smallint, pg_relation_filepath(t)' +language sql +execute on all segments; +select '\! (stat --format=''%s'' ' || string_agg(full_path_relfilenode, ' ') || ' 2>/dev/null || echo 0) | awk ''{sum += $1} END {print sum}''' check_segfiles_size from +(select d.datadir || '/' || f.filepath || '.*' as full_path_relfilenode from getTableSegFiles('table_ao_col'::regclass) f join gp_segment_configuration d +on f.gp_contentid = d.content where d.content <> -1 and d.role = 'p')t +\gset +delete from table_ao_col where true; +vacuum table_ao_col; +:check_segfiles_size +0 +drop function getTableSegFiles(t regclass, out gp_contentid smallint, out filepath text); +drop table table_ao_col; diff --git a/src/test/regress/sql/vacuum_full_ao.sql b/src/test/regress/sql/vacuum_full_ao.sql index e52595a77f1a..240a2f1ad739 100644 --- a/src/test/regress/sql/vacuum_full_ao.sql +++ b/src/test/regress/sql/vacuum_full_ao.sql @@ -22,3 +22,32 @@ select pg_relation_size((select segrelid from pg_appendonly where relid = 'vfao' vacuum full vfao; select pg_relation_size((select segrelid from pg_appendonly where relid = 'vfao'::regclass)) from gp_dist_random('gp_id') where gp_segment_id = 1; + +-- Test that vacuum can process segment files created in an aborted transaction +create table table_ao_col (i int, j int, k int) with (appendonly='true', orientation="column"); + +begin; +insert into table_ao_col select i, i + 1, i + 2 from generate_series(1, 20) i; + +\! psql regression -c 'begin; insert into table_ao_col select i, i + 1, i + 2 from generate_series(1, 20) i; rollback;' + +commit; + +create or replace function getTableSegFiles(t regclass, out gp_contentid smallint, out filepath text) +as 'select current_setting(''gp_contentid'')::smallint, pg_relation_filepath(t)' +language sql +execute on all segments; + +select '\! (stat --format=''%s'' ' || string_agg(full_path_relfilenode, ' ') || ' 2>/dev/null || echo 0) | awk ''{sum += $1} END {print sum}''' check_segfiles_size from +(select d.datadir || '/' || f.filepath || '.*' as full_path_relfilenode from getTableSegFiles('table_ao_col'::regclass) f join gp_segment_configuration d +on f.gp_contentid = d.content where d.content <> -1 and d.role = 'p')t +\gset + +delete from table_ao_col where true; + +vacuum table_ao_col; + +:check_segfiles_size + +drop function getTableSegFiles(t regclass, out gp_contentid smallint, out filepath text); +drop table table_ao_col; From 97d1193527515b604e52eae0509a6770c76633f8 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Tue, 4 Jul 2023 17:57:03 +0300 Subject: [PATCH 026/111] Ensure that creation of an empty relfile is fsync'd at checkpoint. If you create a table and don't insert any data into it, the relation file is never fsync'd. You don't lose data, because an empty table doesn't have any data to begin with, but if you crash and lose the file, subsequent operations on the table will fail with "could not open file" error. To fix, register an fsync request in mdcreate(), like we do for mdwrite(). Per discussion, we probably should also fsync the containing directory after creating a new file. But that's a separate and much wider issue. Backpatch to all supported versions. Reviewed-by: Andres Freund, Thomas Munro Discussion: https://www.postgresql.org/message-id/d47d8122-415e-425c-d0a2-e0160829702d%40iki.fi 6X changes: In 6X, the smgr_which field is not used and not set. This patch adds setting this field. To maintain binary compatibility, this field is set outside the smgropen function. (cherry picked from commit 1b4f1c6f8a6c0b764636f25e17d37d8cab7e82be) --- src/backend/access/transam/xlogutils.c | 1 + src/backend/catalog/storage.c | 5 +++++ src/backend/cdb/cdbappendonlystoragewrite.c | 1 + src/backend/commands/tablecmds.c | 1 + src/backend/storage/buffer/bufmgr.c | 2 ++ src/backend/storage/buffer/localbuf.c | 1 + src/backend/storage/smgr/md.c | 5 +++++ src/include/storage/smgr.h | 6 ++++-- src/include/utils/rel.h | 8 +++++++- 9 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c index b2e915c12c8d..a90400377df5 100644 --- a/src/backend/access/transam/xlogutils.c +++ b/src/backend/access/transam/xlogutils.c @@ -313,6 +313,7 @@ XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum, /* Open the relation at smgr level */ smgr = smgropen(rnode, InvalidBackendId); + smgr->smgr_which = RELSTORAGE_HEAP; /* * Create the target file if it doesn't already exist. This lets us cope diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c index e10be851a33b..4c64261be26f 100644 --- a/src/backend/catalog/storage.c +++ b/src/backend/catalog/storage.c @@ -114,6 +114,7 @@ RelationCreateStorage(RelFileNode rnode, char relpersistence, char relstorage) } srel = smgropen(rnode, backend); + srel->smgr_which = relstorage; smgrcreate(srel, MAIN_FORKNUM, false); if (needs_wal) @@ -372,6 +373,7 @@ smgrDoPendingDeletes(bool isCommit) srel = smgropen(pending->relnode.node, pending->relnode.isTempRelation ? TempRelBackendId : InvalidBackendId); + srel->smgr_which = 0; /* allocate the initial array, or extend it, if needed */ if (maxrels == 0) @@ -541,6 +543,7 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record) SMgrRelation reln; reln = smgropen(xlrec->rnode, InvalidBackendId); + reln->smgr_which = 0; smgrcreate(reln, xlrec->forkNum, true); } else if (info == XLOG_SMGR_CREATE_PDL) @@ -563,6 +566,7 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record) }; SMgrRelation reln = smgropen(xlrec->createrec.rnode, InvalidBackendId); + reln->smgr_which = xlrec->relstorage; smgrcreate(reln, xlrec->createrec.forkNum, true); PdlRedoAdd(&pd); @@ -574,6 +578,7 @@ smgr_redo(XLogRecPtr beginLoc, XLogRecPtr lsn, XLogRecord *record) Relation rel; reln = smgropen(xlrec->rnode, InvalidBackendId); + reln->smgr_which = 0; /* * Forcibly create relation if it doesn't exist (which suggests that diff --git a/src/backend/cdb/cdbappendonlystoragewrite.c b/src/backend/cdb/cdbappendonlystoragewrite.c index 8ed3eba8446f..c25d77ba3c71 100755 --- a/src/backend/cdb/cdbappendonlystoragewrite.c +++ b/src/backend/cdb/cdbappendonlystoragewrite.c @@ -266,6 +266,7 @@ AppendOnlyStorageWrite_TransactionCreateFile(AppendOnlyStorageWrite *storageWrit SMgrRelation reln; reln = smgropen(relFileNode->node, relFileNode->backend); + reln->smgr_which = RELSTORAGE_AOROWS; /* The file might already exist. that's OK */ // WALREP_FIXME: Pass isRedo == true, so that you don't get an error if it diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f8ed7cdd0141..e211391fb77c 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -13156,6 +13156,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) newrnode.relNode = newrelfilenode; newrnode.spcNode = newTableSpace; dstrel = smgropen(newrnode, rel->rd_backend); + dstrel->smgr_which = rel->rd_rel->relstorage; RelationOpenSmgr(rel); diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 399649affcab..0c2907f1cedd 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -361,6 +361,7 @@ ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum, bool hit; SMgrRelation smgr = smgropen(rnode, InvalidBackendId); + smgr->smgr_which = 0; Assert(InRecovery); @@ -2121,6 +2122,7 @@ FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln) reln = smgropen(buf->tag.rnode, istemp ? TempRelBackendId : InvalidBackendId); + reln->smgr_which = 0; } TRACE_POSTGRESQL_BUFFER_FLUSH_START(buf->tag.forkNum, diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 1b77a83c6813..8b0ba5b392a6 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -207,6 +207,7 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, /* Find smgr relation for buffer */ oreln = smgropen(bufHdr->tag.rnode, MyBackendId); + oreln->smgr_which = 0; // GPDB_93_MERGE_FIXME: is this TODO comment still relevant? // UNDONE: Unfortunately, I think we write temp relations to the mirror... diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c index 9ec98c84b2f3..87271a33c0e7 100644 --- a/src/backend/storage/smgr/md.c +++ b/src/backend/storage/smgr/md.c @@ -342,6 +342,9 @@ mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo) reln->md_fd[forkNum]->mdfd_vfd = fd; reln->md_fd[forkNum]->mdfd_segno = 0; reln->md_fd[forkNum]->mdfd_chain = NULL; + + if (!SmgrIsTemp(reln) && reln->smgr_which == RELSTORAGE_HEAP) + register_dirty_segment(reln, forkNum, reln->md_fd[forkNum]); } /* @@ -1326,6 +1329,7 @@ mdsync(void) * cases we couldn't safely do that.) */ reln = smgropen(entry->rnode, InvalidBackendId); + reln->smgr_which = 0; if (entry->is_ao_segnos) { @@ -1914,6 +1918,7 @@ DropRelationFiles(RelFileNodePendingDelete *delrels, int ndelrels, bool isRedo) * given relfile since we don't tie temp relations to their backends. */ SMgrRelation srel = smgropen(delrels[i].node, delrels[i].isTempRelation ? TempRelBackendId : InvalidBackendId); + srel->smgr_which = delrels[i].relstorage; if (isRedo) { diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h index eca15813302a..31354c8bbe88 100644 --- a/src/include/storage/smgr.h +++ b/src/include/storage/smgr.h @@ -63,12 +63,14 @@ typedef struct SMgrRelationData /* additional public fields may someday exist here */ + /* Obsolete storage manager selector, should not be used for any particular purpose */ + /* In GGDB, this field is used to store the table type: Heap or AO . */ + int smgr_which; + /* * Fields below here are intended to be private to smgr.c and its * submodules. Do not touch them from elsewhere. */ - /* Obsolete storage manager selector, should not be used for any particular purpose */ - int smgr_which; /* for md.c; NULL for forks that are not open */ struct _MdfdVec *md_fd[MAX_FORKNUM + 1]; diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 24d39757f4cc..308b181565bc 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -451,6 +451,9 @@ typedef struct ViewOptions #define RelationIsMapped(relation) \ ((relation)->rd_rel->relfilenode == InvalidOid) +#define RelationStorageIsAO(relation) \ + (RelationIsAoRows(relation) || RelationIsAoCols(relation)) + /* * RelationOpenSmgr * Open the relation at the smgr level, if not already done. @@ -458,7 +461,10 @@ typedef struct ViewOptions #define RelationOpenSmgr(relation) \ do { \ if ((relation)->rd_smgr == NULL) \ - smgrsetowner(&((relation)->rd_smgr), smgropen((relation)->rd_node, (relation)->rd_backend)); \ + { \ + smgrsetowner(&((relation)->rd_smgr), smgropen((relation)->rd_node, (relation)->rd_backend)); \ + (relation)->rd_smgr->smgr_which = RelationStorageIsAO(relation) ? RELSTORAGE_AOROWS : RELSTORAGE_HEAP; \ + } \ } while (0) /* From 23f88d8b166cf658561319d461ba9a3665178dc6 Mon Sep 17 00:00:00 2001 From: mgaidai-arenadata Date: Wed, 26 Nov 2025 12:16:45 +0300 Subject: [PATCH 027/111] GG-16: Fix the package building pipeline for external PRs Target the following reusable workflows to v9: - behave-tests - regression-tests - orca-tests - upload - package Ticket: [GG-16](https://tracker.yandex.ru/GG-16) --- .github/workflows/greengage-ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index 85127375d712..0ba7784523a1 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -41,7 +41,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v8 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v9 with: version: 6 target_os: ${{ matrix.target_os }} @@ -59,7 +59,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-regression.yml@v8 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-regression.yml@v9 with: version: 6 target_os: ${{ matrix.target_os }} @@ -77,7 +77,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-orca.yml@v8 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-orca.yml@v9 with: version: 6 target_os: ${{ matrix.target_os }} @@ -113,7 +113,7 @@ jobs: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-upload.yml@v8 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-upload.yml@v9 with: version: 6 target_os: ${{ matrix.target_os }} @@ -133,7 +133,7 @@ jobs: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-package.yml@v7 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-package.yml@v9 with: version: 6 target_os: ${{ matrix.target_os }} From 881e3d3d2634ebe52401406aefceebc9d81c3bb3 Mon Sep 17 00:00:00 2001 From: Grigorii Heifetz Date: Wed, 26 Nov 2025 14:57:55 +0300 Subject: [PATCH 028/111] Fix escaping for perfmon metrics export (#121) Fix escaping for perfmon metrics export What occurs? Whenever new log rows, containing database names with some special characters, were tried to be uploaded from _queries_history.dat file to the queries_history external table an error considering the size of a db column would occur. It would prevent any new logs to be loaded whatsoever. Why it occurs? The db column has a type of VARCHAR(64), thus it means that a larger string was tried to be put. Obvious reason for this - incorrect escaping or lack of such whatsoever. Only two symbols were observed to lead to errors: " and |. In case of a pipe - it leads to another error about incorrect row structure, which is logical, as pipes are used as delimiters inside the file. How do we fix it? Before the patch (1f67d39) db field used to be written to _queries_history.dat without double quotes, so no escaping was present. Now it has it, as we enclose every database name in double quotes. Also, we double the already present in database name double quotes. Does it fix the problem? Yes, as now we escape the whole database name string with all of its special (or not) characters - the very same method is used for the SQL query command and it works. Doubling double quotes is needed as we need to escape the quotes to not to end the string to early. What was changed? Minor code additions for escaping inside the src/backend/gpmon/gpmon.c Tests? New BDD auto test to check that before mentioned logs are added to queries_history correctly (as they appear there). But, not all symbols can be auto tested using present testing functions: ', | and UTF-8 symbols. Errors occur at different steps of a test for different type of symbol and are connected to the way commands are issued to the DBSM. Nevertheless, during hand testing these symbols passed. Observations? dbuser column also is not escaped, but I have not managed to recreate the same issue with it. Yet, it may be worth to add escaping to it in future, but now it seems like an extremely edge case scenario. Ticket: ADBDEV-7272 --------- Co-authored-by: Vladimir Sarmin Co-authored-by: Georgy Shelkovy --- .../test/behave/mgmt_utils/gpperfmon.feature | 19 +++++++ .../behave/mgmt_utils/steps/mgmt_utils.py | 55 +++++++++++++++++++ src/backend/utils/gpmon/gpmon.c | 12 +++- 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/gpMgmt/test/behave/mgmt_utils/gpperfmon.feature b/gpMgmt/test/behave/mgmt_utils/gpperfmon.feature index d96e01063ae5..97900095468f 100644 --- a/gpMgmt/test/behave/mgmt_utils/gpperfmon.feature +++ b/gpMgmt/test/behave/mgmt_utils/gpperfmon.feature @@ -121,6 +121,25 @@ Feature: gpperfmon | debug4 | true | | warning | false | + @gpperfmon_query_history + Scenario Outline: gpperfmon logs db with special characters in name + Given gpperfmon is configured and running in qamode + # escape bugs can reproduce themselves, unless we clear queries_tail + Given _queries_tail.dat is not clogged + Given database with special characters "" is created if not exists + When the user truncates "queries_history" tables in "gpperfmon" + When below sql is executed in "" db + """ + SELECT pg_sleep(20); + """ + Then wait until the history of database with special characters "" appears + And database with special characters "" is dropped if exists + + Examples: + | dbname | + | "ab""c | + | a1!@#$%^ &*()_-+={}\;:"",<.>/? | + @gpperfmon_system_history Scenario: gpperfmon adds to system_history table Given gpperfmon is configured and running in qamode diff --git a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py index 59bf568f5da3..41d188da5010 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py @@ -2196,6 +2196,29 @@ def impl(context, dbname): drop_database_if_exists(context, dbname) create_database(context, dbname) +@given('database with special characters "{dbname}" is created if not exists') +@when('database with special characters "{dbname}" is created if not exists') +@then('database with special characters "{dbname}" is created if not exists') +def impl(context, dbname): + context.exception = None + if not check_db_exists(dbname.replace("'", "''")): + createdb_cmd = 'psql -d postgres -c \'CREATE DATABASE "%s";\'' % dbname.replace('"', '""') + run_command(context, createdb_cmd) + if context.exception: + raise context.exception + +@given('database with special characters "{dbname}" is dropped if exists') +@when('database with special characters "{dbname}" is dropped if exists') +@then('database with special characters "{dbname}" is dropped if exists') +def impl(context, dbname): + context.exception = None + if check_db_exists(dbname.replace("'", "''")): + dropdb_cmd = 'psql -d postgres -c \'DROP DATABASE "%s";\'' % dbname.replace('"', '""') + run_command(context, dropdb_cmd) + if context.exception: + raise context.exception + + @then('validate gpcheckcat logs contain skipping ACL and Owner tests') def imp(context): dirname = 'gpAdminLogs' @@ -2513,6 +2536,30 @@ def impl(context, sql, boolean): if _str2bool(result) != _str2bool(boolean): raise Exception("sql output '%s' is not same as '%s'" % (result, boolean)) +@then('wait until the history of database with special characters "{dbname}" appears') +def impl(context, dbname): + escape_dbname = dbname.replace('"', '\\"') + cmd = Command(name='psql', cmdStr='psql --tuples-only -d gpperfmon -c "select count(*) > 0 from queries_history where db=\'%s\';"' % escape_dbname) + start_time = current_time = datetime.now() + result = None + while (current_time - start_time).seconds < 120: + cmd.run() + if cmd.get_return_code() != 0: + break + result = cmd.get_stdout() + if _str2bool(result): + break + time.sleep(2) + current_time = datetime.now() + + if cmd.get_return_code() != 0: + context.ret_code = cmd.get_return_code() + context.error_message = 'psql internal error: %s' % cmd.get_stderr() + check_return_code(context, 0) + else: + if not _str2bool(result): + raise Exception("history of '%s' did not appear" % (dbname)) + @then('check that the result from boolean sql "{sql}" is "{boolean}"') def impl(context, sql, boolean): cmd = Command(name='psql', cmdStr='psql --tuples-only -d gpperfmon -c "%s"' % sql) @@ -2634,6 +2681,14 @@ def impl(context): raise Exception("File: %s is empty" % gpdb_alert_file_path_src) +@given("_queries_tail.dat is not clogged") +def impl(context): + filename = '%s/gpperfmon/data/_queries_tail.dat' % os.getenv("MASTER_DATA_DIRECTORY") + with open(filename, 'w') as f: + f.truncate() + filename = '%s/gpperfmon/data/queries_tail.dat' % os.getenv("MASTER_DATA_DIRECTORY") + with open(filename, 'w') as f: + f.truncate() @then('the file with the fake timestamp no longer exists') def impl(context): diff --git a/src/backend/utils/gpmon/gpmon.c b/src/backend/utils/gpmon/gpmon.c index 96217a09a098..2bc0d50923d6 100644 --- a/src/backend/utils/gpmon/gpmon.c +++ b/src/backend/utils/gpmon/gpmon.c @@ -16,6 +16,7 @@ #include "utils/guc.h" #include "utils/memutils.h" +#include "utils/builtins.h" #include "cdb/cdbtm.h" #include "cdb/cdbvars.h" @@ -222,6 +223,7 @@ void gpmon_qlog_packet_init(gpmon_packet_t *gpmonPacket) { const char *username = NULL; char *dbname = NULL; + char *escaped_dbname = NULL; Assert(gp_enable_gpperfmon && Gp_role == GP_ROLE_DISPATCH); Assert(gpmonPacket); @@ -241,7 +243,15 @@ void gpmon_qlog_packet_init(gpmon_packet_t *gpmonPacket) /* DB Id */ dbname = get_database_name(MyDatabaseId); /* needs to be freed */ - snprintf(gpmonPacket->u.qlog.db, sizeof(gpmonPacket->u.qlog.db), "%s", dbname ? dbname : ""); + if (dbname) + escaped_dbname = quote_identifier(dbname); + snprintf(gpmonPacket->u.qlog.db, sizeof(gpmonPacket->u.qlog.db), "%s", + escaped_dbname ? escaped_dbname : ""); + if (escaped_dbname != dbname) + { + pfree(escaped_dbname); + escaped_dbname = NULL; + } if(dbname) { pfree(dbname); From 3bd88175e9906db0bef54b64ac944cc5eda0f32c Mon Sep 17 00:00:00 2001 From: Georgy Shelkovy Date: Thu, 27 Nov 2025 00:01:00 +0500 Subject: [PATCH 029/111] Fix inconsistency DROP IF EXISTS command (#130) When the DROP IF EXISTS command is called, it is unconditionally dispatched to the segments, even if the object doesn't exist on the coordinator. This can lead to inconsistencies, for example: - the first session starts a table drop on the coordinator. Even though the table doesn't exist, the drop is still dispatched to the segments. - at the same time, the second session creates the table (on the coordinator and on the segments). - the first session (on the segments) already sees the table and therefore deletes it. Don't dispatch DROP if object doesn't exist on coordinator. Exclude the drop_rename test from the parallel group because it contains fault-injectors. Add a new GUC, gp_dispatch_drop_always, for unconditional DROP dispatch even if the object isn't present on the coordinator, as this functionality is used by the gpcheckcat utility, for example, in the orphan_temp_table test. Ticket: ADBDEV-8867 --- .abi-check/6.29.1/postgres.symbols.ignore | 1 + gpMgmt/bin/gpcheckcat | 1 + src/backend/commands/dropcmds.c | 15 +++- src/backend/commands/tablecmds.c | 15 +++- src/backend/tcop/utility.c | 10 ++- src/backend/utils/misc/guc_gp.c | 13 +++ src/include/utils/guc.h | 2 + src/include/utils/unsync_guc_name.h | 1 + src/test/isolation2/expected/drop_rename.out | 82 +++++++++++++++++++ src/test/isolation2/isolation2_schedule | 3 +- src/test/isolation2/sql/drop_rename.sql | 34 ++++++++ src/test/regress/expected/direct_dispatch.out | 1 - .../expected/direct_dispatch_optimizer.out | 1 - 13 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 .abi-check/6.29.1/postgres.symbols.ignore diff --git a/.abi-check/6.29.1/postgres.symbols.ignore b/.abi-check/6.29.1/postgres.symbols.ignore new file mode 100644 index 000000000000..aa4c33e2be25 --- /dev/null +++ b/.abi-check/6.29.1/postgres.symbols.ignore @@ -0,0 +1 @@ +ConfigureNamesBool_gp diff --git a/gpMgmt/bin/gpcheckcat b/gpMgmt/bin/gpcheckcat index 5188b13a5509..f031fbb3a874 100755 --- a/gpMgmt/bin/gpcheckcat +++ b/gpMgmt/bin/gpcheckcat @@ -325,6 +325,7 @@ def connect(user=None, password=None, host=None, port=None, '''Connect to DB using parameters in GV''' # make search path safe options = '-c search_path=' + options += ' -c gp_dispatch_drop_always=on' if utilityMode: options += ' -c gp_session_role=utility' for val in GV.opt['-x']: diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c index 984163fa5a78..c43c3dc9fa6e 100644 --- a/src/backend/commands/dropcmds.c +++ b/src/backend/commands/dropcmds.c @@ -26,6 +26,7 @@ #include "nodes/makefuncs.h" #include "parser/parse_type.h" #include "utils/builtins.h" +#include "utils/guc.h" #include "utils/syscache.h" #include "cdb/cdbvars.h" @@ -57,10 +58,12 @@ RemoveObjects(DropStmt *stmt) ObjectAddresses *objects; ListCell *cell1; ListCell *cell2 = NULL; + ListCell *next; + ListCell *prev = NULL; objects = new_object_addresses(); - foreach(cell1, stmt->objects) + for (cell1 = list_head(stmt->objects); cell1; cell1 = next) { ObjectAddress address; List *objname = lfirst(cell1); @@ -68,6 +71,8 @@ RemoveObjects(DropStmt *stmt) Relation relation = NULL; Oid namespaceId; + next = lnext(cell1); + if (stmt->arguments) { cell2 = (!cell2 ? list_head(stmt->arguments) : lnext(cell2)); @@ -90,6 +95,12 @@ RemoveObjects(DropStmt *stmt) { Assert(stmt->missing_ok); does_not_exist_skipping(stmt->removeType, objname, objargs); + + if (gp_dispatch_drop_always) + prev = cell1; + else + stmt->objects = list_delete_cell(stmt->objects, cell1, prev); + continue; } @@ -129,6 +140,8 @@ RemoveObjects(DropStmt *stmt) heap_close(relation, NoLock); add_exact_object_address(&address, objects); + + prev = cell1; } /* Here we really delete them. */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index e211391fb77c..b31f722a56be 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1199,6 +1199,8 @@ RemoveRelations(DropStmt *drop) ObjectAddresses *objects; char relkind; ListCell *cell; + ListCell *next; + ListCell *prev = NULL; int flags = 0; LOCKMODE lockmode = AccessExclusiveLock; @@ -1266,13 +1268,15 @@ RemoveRelations(DropStmt *drop) /* Lock and validate each relation; build a list of object addresses */ objects = new_object_addresses(); - foreach(cell, drop->objects) + for (cell = list_head(drop->objects); cell; cell = next) { RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell)); Oid relOid; ObjectAddress obj; struct DropRelationCallbackState state; + next = lnext(cell); + /* * These next few steps are a great deal like relation_openrv, but we * don't bother building a relcache entry since we don't need it. @@ -1298,6 +1302,13 @@ RemoveRelations(DropStmt *drop) if (!OidIsValid(relOid)) { DropErrorMsgNonExistent(rel, relkind, drop->missing_ok); + Assert(drop->missing_ok); + + if (gp_dispatch_drop_always) + prev = cell; + else + drop->objects = list_delete_cell(drop->objects, cell, prev); + continue; } @@ -1339,6 +1350,8 @@ RemoveRelations(DropStmt *drop) obj.objectSubId = 0; add_exact_object_address(&obj, objects); + + prev = cell; } performMultipleDeletions(objects, drop->behavior, flags); diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index a3b6efd9ca5d..7a100e9b22ce 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1840,19 +1840,23 @@ ExecDropStmt(DropStmt *stmt, bool isTopLevel) case OBJECT_MATVIEW: case OBJECT_FOREIGN_TABLE: case OBJECT_EXTTABLE: - RemoveRelations(stmt); + RemoveRelations(copyStmt); break; default: - RemoveObjects(stmt); + RemoveObjects(copyStmt); break; } + SIMPLE_FAULT_INJECTOR("wait_before_drop_dispatch"); + /* * Dispatch the original, unmodified statement. * * Event triggers are not stored in QE nodes, so skip those. */ - if (Gp_role == GP_ROLE_DISPATCH && stmt->removeType != OBJECT_EVENT_TRIGGER) + if (Gp_role == GP_ROLE_DISPATCH && + stmt->removeType != OBJECT_EVENT_TRIGGER && + list_length(copyStmt->objects) > 0) { int flags; diff --git a/src/backend/utils/misc/guc_gp.c b/src/backend/utils/misc/guc_gp.c index 5cbeea560258..a5ad46ce88b8 100644 --- a/src/backend/utils/misc/guc_gp.c +++ b/src/backend/utils/misc/guc_gp.c @@ -482,6 +482,8 @@ bool gp_allow_date_field_width_5digits = false; bool gp_track_pending_delete = true; +bool gp_dispatch_drop_always = false; + /* GUC to set interval for streaming archival status */ int wal_sender_archiving_status_interval; @@ -3434,6 +3436,17 @@ struct config_bool ConfigureNamesBool_gp[] = NULL, NULL, NULL }, + { + {"gp_dispatch_drop_always", PGC_USERSET, DEVELOPER_OPTIONS, + gettext_noop("Dispatch DROP if object doesn't exist on coordinator"), + NULL, + GUC_DISALLOW_IN_FILE | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE + }, + &gp_dispatch_drop_always, + false, + NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 712fd7bc6557..188c36555b61 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -604,6 +604,8 @@ extern bool gp_allow_date_field_width_5digits; extern bool gp_track_pending_delete; +extern bool gp_dispatch_drop_always; + typedef enum { INDEX_CHECK_NONE, diff --git a/src/include/utils/unsync_guc_name.h b/src/include/utils/unsync_guc_name.h index 1dcad86fcc01..c996235f8f5a 100644 --- a/src/include/utils/unsync_guc_name.h +++ b/src/include/utils/unsync_guc_name.h @@ -566,3 +566,4 @@ "xmloption", "zero_damaged_pages", "gp_track_pending_delete", + "gp_dispatch_drop_always", diff --git a/src/test/isolation2/expected/drop_rename.out b/src/test/isolation2/expected/drop_rename.out index af0916cb2939..db6e14d0a75f 100644 --- a/src/test/isolation2/expected/drop_rename.out +++ b/src/test/isolation2/expected/drop_rename.out @@ -110,3 +110,85 @@ DROP ERROR: relation "t3" does not exist LINE 1: select count(*) from t3; ^ + +-- Ensure DROP doesn't make inconsistency +-- start_ignore +drop table if exists t4; +DROP +-- end_ignore +select gp_inject_fault('wait_before_drop_dispatch', 'suspend', 1); + gp_inject_fault +----------------- + Success: +(1 row) +1&:drop table if exists t4; +create table t4 (a int, b text) distributed by (a); +CREATE +select gp_wait_until_triggered_fault('wait_before_drop_dispatch', 1, 1); + gp_wait_until_triggered_fault +------------------------------- + Success: +(1 row) +select gp_inject_fault('wait_before_drop_dispatch', 'reset', 1); + gp_inject_fault +----------------- + Success: +(1 row) +1<: <... completed> +DROP +drop table t4; +DROP + +-- start_ignore +drop type if exists t5; +DROP +-- end_ignore +select gp_inject_fault('wait_before_drop_dispatch', 'suspend', 1); + gp_inject_fault +----------------- + Success: +(1 row) +1&:drop type if exists t5; +create type t5 as (a int, b text); +CREATE +select gp_wait_until_triggered_fault('wait_before_drop_dispatch', 1, 1); + gp_wait_until_triggered_fault +------------------------------- + Success: +(1 row) +select gp_inject_fault('wait_before_drop_dispatch', 'reset', 1); + gp_inject_fault +----------------- + Success: +(1 row) +1<: <... completed> +DROP +drop type t5; +DROP + +-- start_ignore +drop schema if exists t6; +DROP +-- end_ignore +select gp_inject_fault('wait_before_drop_dispatch', 'suspend', 1); + gp_inject_fault +----------------- + Success: +(1 row) +1&:drop schema if exists t6; +create schema t6; +CREATE +select gp_wait_until_triggered_fault('wait_before_drop_dispatch', 1, 1); + gp_wait_until_triggered_fault +------------------------------- + Success: +(1 row) +select gp_inject_fault('wait_before_drop_dispatch', 'reset', 1); + gp_inject_fault +----------------- + Success: +(1 row) +1<: <... completed> +DROP +drop schema t6; +DROP diff --git a/src/test/isolation2/isolation2_schedule b/src/test/isolation2/isolation2_schedule index 80c99ffff443..ec9f4c73e352 100644 --- a/src/test/isolation2/isolation2_schedule +++ b/src/test/isolation2/isolation2_schedule @@ -68,7 +68,8 @@ test: deadlock_under_entry_db_singleton # conflict when running in parallel with other cases. test: misc -test: starve_case pg_views_concurrent_drop alter_blocks_for_update_and_viceversa drop_rename reader_waits_for_lock resource_queue +test: drop_rename +test: starve_case pg_views_concurrent_drop alter_blocks_for_update_and_viceversa reader_waits_for_lock resource_queue test: vacuum_drop_phase_ao # Test simple cancellation for resource queues and cancellation/deadlocks for diff --git a/src/test/isolation2/sql/drop_rename.sql b/src/test/isolation2/sql/drop_rename.sql index 6cd6115f036c..ecede5a8f531 100644 --- a/src/test/isolation2/sql/drop_rename.sql +++ b/src/test/isolation2/sql/drop_rename.sql @@ -54,3 +54,37 @@ 3<: 2<: 2:select count(*) from t3; + +-- Ensure DROP doesn't make inconsistency +-- start_ignore +drop table if exists t4; +-- end_ignore +select gp_inject_fault('wait_before_drop_dispatch', 'suspend', 1); +1&:drop table if exists t4; +create table t4 (a int, b text) distributed by (a); +select gp_wait_until_triggered_fault('wait_before_drop_dispatch', 1, 1); +select gp_inject_fault('wait_before_drop_dispatch', 'reset', 1); +1<: +drop table t4; + +-- start_ignore +drop type if exists t5; +-- end_ignore +select gp_inject_fault('wait_before_drop_dispatch', 'suspend', 1); +1&:drop type if exists t5; +create type t5 as (a int, b text); +select gp_wait_until_triggered_fault('wait_before_drop_dispatch', 1, 1); +select gp_inject_fault('wait_before_drop_dispatch', 'reset', 1); +1<: +drop type t5; + +-- start_ignore +drop schema if exists t6; +-- end_ignore +select gp_inject_fault('wait_before_drop_dispatch', 'suspend', 1); +1&:drop schema if exists t6; +create schema t6; +select gp_wait_until_triggered_fault('wait_before_drop_dispatch', 1, 1); +select gp_inject_fault('wait_before_drop_dispatch', 'reset', 1); +1<: +drop schema t6; diff --git a/src/test/regress/expected/direct_dispatch.out b/src/test/regress/expected/direct_dispatch.out index 42e3ef6d97a3..b7c104c9e128 100644 --- a/src/test/regress/expected/direct_dispatch.out +++ b/src/test/regress/expected/direct_dispatch.out @@ -658,7 +658,6 @@ INFO: Distributed transaction command 'Distributed Commit Prepared' to ALL cont -- the tests will fail. drop table if exists bar_randDistr; NOTICE: table "bar_randdistr" does not exist, skipping -INFO: Distributed transaction command 'Distributed Commit (one-phase)' to ALL contents: 0 1 2 create table bar_randDistr(col1 int, col2 int) distributed randomly; INFO: Distributed transaction command 'Distributed Prepare' to ALL contents: 0 1 2 INFO: Distributed transaction command 'Distributed Commit Prepared' to ALL contents: 0 1 2 diff --git a/src/test/regress/expected/direct_dispatch_optimizer.out b/src/test/regress/expected/direct_dispatch_optimizer.out index 69fbfbd80c62..8e79cfcd792d 100644 --- a/src/test/regress/expected/direct_dispatch_optimizer.out +++ b/src/test/regress/expected/direct_dispatch_optimizer.out @@ -679,7 +679,6 @@ INFO: Distributed transaction command 'Distributed Commit Prepared' to ALL cont -- the tests will fail. drop table if exists bar_randDistr; NOTICE: table "bar_randdistr" does not exist, skipping -INFO: Distributed transaction command 'Distributed Commit (one-phase)' to ALL contents: 0 1 2 create table bar_randDistr(col1 int, col2 int) distributed randomly; INFO: Distributed transaction command 'Distributed Prepare' to ALL contents: 0 1 2 INFO: Distributed transaction command 'Distributed Commit Prepared' to ALL contents: 0 1 2 From 13b86fa082c4f82d612fe5d377eaad50e8b16971 Mon Sep 17 00:00:00 2001 From: Victor Wagner Date: Thu, 27 Nov 2025 09:48:33 +0300 Subject: [PATCH 030/111] Fix subplans with correlated functions or master-only or replicated tables (#89) The add_rte_to_flat_rtable function places a RangeTblEntry into the glob->finalrtable table with a zeroed list of functions. The ParallelizeCorrelatedSubPlanMutator function uses ctx->rtable, which is populated by the root->glob->finalrtable function in the ParallelizeCorrelatedSubPlan function. Therefore, in the ParallelizeCorrelatedSubPlanMutator function, the rte->functions list is always empty. Therefore, the old condition for checking whether functions are correlated did not work. Now, in the ParallelizeCorrelatedSubPlanMutator function, functions are available in the fscan->functions list, which is where they are taken for correlation and location checking. Correlated non-any functions are prohibited in subplans. For other combinations, we now add broadcasting and materialization. Another issue arose with correlated subplans with master-only or replicated tables. The existing condition checked the scan locus only, without checking the requested locus. As a result, the table scan was performed on segments, which is unacceptable for the gp_segment_configuration master-only table. Now, if the requested locus for such tables and the scan locus are not equal to the entry, we again add broadcasting and materialization. Co-authored-by: Georgy Shelkovy Co-authored-by: Maxim Michkov Ticket: ADBDEV-6884 --- src/backend/cdb/cdbllize.c | 50 +- src/test/regress/expected/rangefuncs.out | 6 +- src/test/regress/expected/rangefuncs_cdb.out | 8 +- src/test/regress/expected/subselect_gp.out | 3434 ++++++++++++++++ src/test/regress/expected/subselect_gp_1.out | 3434 ++++++++++++++++ .../expected/subselect_gp_optimizer.out | 3535 +++++++++++++++++ src/test/regress/sql/rangefuncs_cdb.sql | 8 +- src/test/regress/sql/subselect_gp.sql | 641 +++ 8 files changed, 11087 insertions(+), 29 deletions(-) diff --git a/src/backend/cdb/cdbllize.c b/src/backend/cdb/cdbllize.c index f860cd09bea3..11eb32f1420f 100644 --- a/src/backend/cdb/cdbllize.c +++ b/src/backend/cdb/cdbllize.c @@ -432,28 +432,16 @@ ParallelizeCorrelatedSubPlanMutator(Node *node, ParallelizeCorrelatedPlanWalkerC if (node == NULL) return NULL; +#ifdef USE_ASSERT_CHECKING if (IsA(node, FunctionScan)) { RangeTblEntry *rte; - ListCell *lc; rte = rt_fetch(((Scan *) node)->scanrelid, ctx->rtable); Assert(rte->rtekind == RTE_FUNCTION); - - foreach(lc, rte->functions) - { - RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); - - if (rtfunc->funcexpr && - ContainsParamWalker(rtfunc->funcexpr, NULL /* ctx */ ) && ctx->subPlanDistributed) - { - ereport(ERROR, - (errcode(ERRCODE_GP_FEATURE_NOT_YET), - errmsg("cannot parallelize that query yet"), - errdetail("In a subquery FROM clause, a function invocation cannot contain a correlated reference."))); - } - } + Assert(rte->functions == NIL); } +#endif /* * If the ModifyTable node appears inside the correlated Subplan, it has @@ -465,6 +453,7 @@ ParallelizeCorrelatedSubPlanMutator(Node *node, ParallelizeCorrelatedPlanWalkerC if (IsA(node, SeqScan) ||IsA(node, ShareInputScan) ||IsA(node, ExternalScan) + ||IsA(node, FunctionScan) ||(IsA(node, SubqueryScan) && IsA(((SubqueryScan *) node)->subplan, ModifyTable)) ||IsA(node,ModifyTable)) { @@ -479,9 +468,29 @@ ParallelizeCorrelatedSubPlanMutator(Node *node, ParallelizeCorrelatedPlanWalkerC * unnest(array[typoutput, typsend]) from pg_type) then 'upg_catalog.' * else 'pg_catalog.' end) FROM pg_proc p; **/ - Assert(scanPlan->flow); + Assert(scanPlan->flow && ctx->currentPlanFlow); + + if (scanPlan->flow->locustype == CdbLocusType_General) + return node; + if (scanPlan->flow->locustype == CdbLocusType_Entry) - return (Node *) node; + { + if (ctx->currentPlanFlow->locustype == CdbLocusType_Entry) + return node; + + if (ctx->currentPlanFlow->locustype == CdbLocusType_General) + return node; + } + + if (IsA(node, FunctionScan)) + { + FunctionScan *fscan = (FunctionScan *) node; + + if (ContainsParamWalker((Node *) fscan->functions, NULL /* ctx */ )) + ereport(ERROR, + (errcode(ERRCODE_GP_FEATURE_NOT_YET), + errmsg("cannot materialize function with correlated parameters"))); + } /** * Steps: @@ -602,6 +611,13 @@ ParallelizeCorrelatedSubPlanMutator(Node *node, ParallelizeCorrelatedPlanWalkerC broadcastPlan(scanPlan, false /* stable */ , false /* rescannable */ , ctx->currentPlanFlow->numsegments /* numsegments */ ); } + else if (ctx->currentPlanFlow->locustype == CdbLocusType_SegmentGeneral && + scanPlan->flow->locustype == CdbLocusType_Entry) + { + /* We still need broadcast from entry-db to one segment. */ + broadcastPlan(scanPlan, false /* stable */ , false /* rescannable */ , + 1 /* numsegments */ ); + } else { focusPlan(scanPlan, false /* stable */ , false /* rescannable */ ); diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 3037e1217e12..7c318b01557c 100755 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -273,16 +273,14 @@ HINT: likely caused by a function that reads or modifies data in a distributed CONTEXT: SQL function "foot" statement 1 -- function in subselect select * from foo2 where f2 in (select f2 from foot(foo2.fooid) z where z.fooid = foo2.fooid) ORDER BY 1,2; -ERROR: function cannot execute on a QE slice because it accesses relation "public.foo2" -CONTEXT: SQL function "foot" during startup +ERROR: cannot materialize function with correlated parameters -- function in subselect select * from foo2 where f2 in (select f2 from foot(1) z where z.fooid = foo2.fooid) ORDER BY 1,2; ERROR: function cannot execute on a QE slice because it accesses relation "public.foo2" CONTEXT: SQL function "foot" during startup -- function in subselect select * from foo2 where f2 in (select f2 from foot(foo2.fooid) z where z.fooid = 1) ORDER BY 1,2; -ERROR: function cannot execute on a QE slice because it accesses relation "public.foo2" -CONTEXT: SQL function "foot" during startup +ERROR: cannot materialize function with correlated parameters -- nested functions select foot.fooid, foot.f2 from foot(sin(pi()/2)::int) ORDER BY 1,2; fooid | f2 diff --git a/src/test/regress/expected/rangefuncs_cdb.out b/src/test/regress/expected/rangefuncs_cdb.out index 4483337a9dd5..761c871f6a8b 100644 --- a/src/test/regress/expected/rangefuncs_cdb.out +++ b/src/test/regress/expected/rangefuncs_cdb.out @@ -47,7 +47,7 @@ BEGIN RETURN NEXT r; RETURN; END -$$ language plpgsql; +$$ language plpgsql IMMUTABLE; -- function in select clause select foost(1); foost @@ -169,7 +169,7 @@ BEGIN RETURN NEXT rec; return; END -$$ LANGUAGE plpgsql; +$$ LANGUAGE plpgsql IMMUTABLE; -- function in select clause -- Fails: plpgsql does not support SFRM_Materialize select foor(1); @@ -278,7 +278,7 @@ BEGIN RETURN NEXT; RETURN; END; -$$ LANGUAGE plpgsql; +$$ LANGUAGE plpgsql IMMUTABLE; -- function in select clause select fooro(1); fooro @@ -386,7 +386,7 @@ BEGIN RETURN NEXT; RETURN; END -$$ language plpgsql; +$$ language plpgsql IMMUTABLE; -- function in select clause select foot(1); foot diff --git a/src/test/regress/expected/subselect_gp.out b/src/test/regress/expected/subselect_gp.out index e0cdb9666fdb..2b8a3d2adaf9 100644 --- a/src/test/regress/expected/subselect_gp.out +++ b/src/test/regress/expected/subselect_gp.out @@ -1,3 +1,7 @@ +-- start_matchsubs +-- m/ \(subselect\.c:\d+\)/ +-- s/ \(subselect\.c:\d+\)// +-- end_matchsubs set optimizer_enable_master_only_queries = on; set optimizer_segments = 3; set optimizer_nestloop_factor = 1.0; @@ -3804,3 +3808,3433 @@ drop table t3; drop table t_repl; drop table t2; drop table t1; +-- Test subplan with correlated functions or master-only tables +create table d(d int) distributed by (d); +insert into d select g from generate_series(0, 9) g; +analyze d; +create table p(p int) distributed replicated; +insert into p select g from generate_series(0, 9) g; +analyze p; +create table r(r int) distributed randomly; +insert into r select g from generate_series(0, 9) g; +analyze r; +create view g as select g from generate_series(0, 9) g; +create view c as select dbid c from gp_segment_configuration c; +explain (verbose, costs off) +select (select c from c limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select c from c where c = g) from g; + QUERY PLAN +----------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Filter: (c.dbid = g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select c from c where c = g limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Filter: (c.dbid = g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset c) from c; + QUERY PLAN +------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select g from g where g = c) from c; + QUERY PLAN +------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = c.dbid) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select g from g where g = c limit 1 offset c) from c; + QUERY PLAN +------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = c.dbid) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select d from d where d = g) from g; + QUERY PLAN +-------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Result + Output: d.d + Filter: (d.d = g.g) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select d from d where d = g limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = g.g) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select g from g where g = d) from d; + QUERY PLAN +------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = d.d) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select g from g where g = d limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = d.d) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(13 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Result + Output: p.p + -> Materialize + Output: p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select p from p where p = g) from g; + QUERY PLAN +-------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Result + Output: p.p + Filter: (p.p = g.g) + -> Materialize + Output: p.p, p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select p from p where p = g limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Result + Output: p.p + Filter: (p.p = g.g) + -> Materialize + Output: p.p, p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset p) from p; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select g from g where g = p) from p; + QUERY PLAN +------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = p.p) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select g from g where g = p limit 1 offset p) from p; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = p.p) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(13 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select r from r where r = g) from g; + QUERY PLAN +-------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Result + Output: r.r + Filter: (r.r = g.g) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select r from r where r = g limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = g.g) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select g from g where g = r) from r; + QUERY PLAN +------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = r.r) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select g from g where g = r limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = r.r) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(13 rows) + +explain (verbose, costs off) +select (select c from c limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + -> Materialize + Output: c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select c from c where c = d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Result + Output: c.dbid + Filter: (c.dbid = d.d) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select c from c where c = d limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + Filter: (c.dbid = d.d) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select d from d where d = c) from c; + QUERY PLAN +-------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Result + Output: d.d + Filter: (d.d = c.dbid) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select d from d where d = c limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = c.dbid) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select c from c limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + -> Materialize + Output: c.dbid + -> Broadcast Motion 1:1 (slice1) + Output: c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select c from c where c = p) from p; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Result + Output: c.dbid + Filter: (c.dbid = p.p) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:1 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select c from c where c = p limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + Filter: (c.dbid = p.p) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:1 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Result + Output: p.p + -> Materialize + Output: p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select p from p where p = c) from c; + QUERY PLAN +-------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Result + Output: p.p + Filter: (p.p = c.dbid) + -> Materialize + Output: p.p, p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select p from p where p = c limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Result + Output: p.p + Filter: (p.p = c.dbid) + -> Materialize + Output: p.p, p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select c from c limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + -> Materialize + Output: c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select c from c where c = r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Result + Output: c.dbid + Filter: (c.dbid = r.r) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select c from c where c = r limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + Filter: (c.dbid = r.r) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select r from r where r = c) from c; + QUERY PLAN +-------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Result + Output: r.r + Filter: (r.r = c.dbid) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select r from r where r = c limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = c.dbid) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select d from d where d = p) from p; + QUERY PLAN +-------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Result + Output: d.d + Filter: (d.d = p.p) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select d from d where d = p limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = p.p) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: p.p + -> Result + Output: p.p + -> Materialize + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select p from p where p = d) from d; + QUERY PLAN +------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Result + Output: p.p + Filter: (p.p = d.d) + -> Materialize + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select p from p where p = d limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: p.p + -> Result + Output: p.p + Filter: (p.p = d.d) + -> Materialize + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + -> Materialize + Output: d.d + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select d from d where d = r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Result + Output: d.d + Filter: (d.d = r.r) + -> Materialize + Output: d.d, d.d + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select d from d where d = r limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = r.r) + -> Materialize + Output: d.d, d.d + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + -> Materialize + Output: r.r + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select r from r where r = d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Result + Output: r.r + Filter: (r.r = d.d) + -> Materialize + Output: r.r, r.r + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select r from r where r = d limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = d.d) + -> Materialize + Output: r.r, r.r + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: p.p + -> Result + Output: p.p + -> Materialize + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select p from p where p = r) from r; + QUERY PLAN +------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Result + Output: p.p + Filter: (p.p = r.r) + -> Materialize + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select p from p where p = r limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: p.p + -> Result + Output: p.p + Filter: (p.p = r.r) + -> Materialize + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select r from r where r = p) from p; + QUERY PLAN +-------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Result + Output: r.r + Filter: (r.r = p.p) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select r from r where r = p limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = p.p) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +create function i(i int) returns setof int language plpgsql immutable as $$ begin return query select i; end $$; +create function s(s int) returns setof int language plpgsql stable as $$ begin return query select s; end $$; +create function v(v int) returns setof int language plpgsql volatile as $$ begin return query select v; end $$; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; + QUERY PLAN +----------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(d.d) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(d.d) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:3 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:3 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; + QUERY PLAN +----------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(p.p) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(p.p) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:1 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:1 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; + QUERY PLAN +----------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(r.r) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(r.r) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:3 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:3 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +alter function i(i int) execute on all segments; +alter function s(s int) execute on all segments; +alter function v(v int) execute on all segments; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Gather Motion 3:1 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Gather Motion 3:1 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Gather Motion 3:1 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Gather Motion 3:1 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Gather Motion 3:1 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Gather Motion 3:1 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Gather Motion 3:1 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Gather Motion 3:1 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Gather Motion 3:1 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +alter function i(i int) execute on master; +alter function s(s int) execute on master; +alter function v(v int) execute on master; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 1:3 (slice1) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:3 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:3 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 1:1 (slice1) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:1 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:1 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 1:3 (slice1) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:3 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:3 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +alter function i(i int) execute on initplan; +alter function s(s int) execute on initplan; +alter function v(v int) execute on initplan; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 2) + SubPlan 2 + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 2) + SubPlan 2 + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 2) + SubPlan 2 + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Result + Output: i_1.i + -> Materialize + Output: i_1.i + -> Broadcast Motion 1:3 (slice1) + Output: i_1.i + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Result + Output: s_1.s + -> Materialize + Output: s_1.s + -> Broadcast Motion 1:3 (slice1) + Output: s_1.s + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Result + Output: v_1.v + -> Materialize + Output: v_1.v + -> Broadcast Motion 1:3 (slice1) + Output: v_1.v + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 2) + Function Call: generate_series(0, 9) + SubPlan 2 + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 2) + Function Call: generate_series(0, 9) + SubPlan 2 + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 2) + Function Call: generate_series(0, 9) + SubPlan 2 + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 1) + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Result + Output: i_1.i + -> Materialize + Output: i_1.i + -> Broadcast Motion 1:1 (slice1) + Output: i_1.i + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 1) + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Result + Output: s_1.s + -> Materialize + Output: s_1.s + -> Broadcast Motion 1:1 (slice1) + Output: s_1.s + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 1) + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Result + Output: v_1.v + -> Materialize + Output: v_1.v + -> Broadcast Motion 1:1 (slice1) + Output: v_1.v + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Result + Output: i_1.i + -> Materialize + Output: i_1.i + -> Broadcast Motion 1:3 (slice1) + Output: i_1.i + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Result + Output: s_1.s + -> Materialize + Output: s_1.s + -> Broadcast Motion 1:3 (slice1) + Output: s_1.s + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Result + Output: v_1.v + -> Materialize + Output: v_1.v + -> Broadcast Motion 1:3 (slice1) + Output: v_1.v + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: plan should not reference subplan's variable +drop function i(a int); +drop function s(a int); +drop function v(a int); +drop table d; +drop table p; +drop table r; +drop view c; +drop view g; diff --git a/src/test/regress/expected/subselect_gp_1.out b/src/test/regress/expected/subselect_gp_1.out index 476392f11b59..cbf549463d1e 100644 --- a/src/test/regress/expected/subselect_gp_1.out +++ b/src/test/regress/expected/subselect_gp_1.out @@ -1,3 +1,7 @@ +-- start_matchsubs +-- m/ \(subselect\.c:\d+\)/ +-- s/ \(subselect\.c:\d+\)// +-- end_matchsubs set optimizer_enable_master_only_queries = on; set optimizer_segments = 3; set optimizer_nestloop_factor = 1.0; @@ -3804,3 +3808,3433 @@ drop table t3; drop table t_repl; drop table t2; drop table t1; +-- Test subplan with correlated functions or master-only tables +create table d(d int) distributed by (d); +insert into d select g from generate_series(0, 9) g; +analyze d; +create table p(p int) distributed replicated; +insert into p select g from generate_series(0, 9) g; +analyze p; +create table r(r int) distributed randomly; +insert into r select g from generate_series(0, 9) g; +analyze r; +create view g as select g from generate_series(0, 9) g; +create view c as select dbid c from gp_segment_configuration c; +explain (verbose, costs off) +select (select c from c limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select c from c where c = g) from g; + QUERY PLAN +----------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Filter: (c.dbid = g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select c from c where c = g limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Filter: (c.dbid = g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset c) from c; + QUERY PLAN +------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select g from g where g = c) from c; + QUERY PLAN +------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = c.dbid) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select g from g where g = c limit 1 offset c) from c; + QUERY PLAN +------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = c.dbid) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select d from d where d = g) from g; + QUERY PLAN +-------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Result + Output: d.d + Filter: (d.d = g.g) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select d from d where d = g limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = g.g) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select g from g where g = d) from d; + QUERY PLAN +------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = d.d) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select g from g where g = d limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = d.d) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(13 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Result + Output: p.p + -> Materialize + Output: p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select p from p where p = g) from g; + QUERY PLAN +-------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Result + Output: p.p + Filter: (p.p = g.g) + -> Materialize + Output: p.p, p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select p from p where p = g limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Result + Output: p.p + Filter: (p.p = g.g) + -> Materialize + Output: p.p, p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset p) from p; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select g from g where g = p) from p; + QUERY PLAN +------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = p.p) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select g from g where g = p limit 1 offset p) from p; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = p.p) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(13 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select r from r where r = g) from g; + QUERY PLAN +-------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Result + Output: r.r + Filter: (r.r = g.g) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select r from r where r = g limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = g.g) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select g from g where g = r) from r; + QUERY PLAN +------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = r.r) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select g from g where g = r limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: g.g + -> Function Scan on pg_catalog.generate_series g + Output: g.g + Function Call: generate_series(0, 9) + Filter: (g.g = r.r) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(13 rows) + +explain (verbose, costs off) +select (select c from c limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + -> Materialize + Output: c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select c from c where c = d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Result + Output: c.dbid + Filter: (c.dbid = d.d) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select c from c where c = d limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + Filter: (c.dbid = d.d) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select d from d where d = c) from c; + QUERY PLAN +-------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Result + Output: d.d + Filter: (d.d = c.dbid) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select d from d where d = c limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = c.dbid) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select c from c limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + -> Materialize + Output: c.dbid + -> Broadcast Motion 1:1 (slice1) + Output: c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select c from c where c = p) from p; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Result + Output: c.dbid + Filter: (c.dbid = p.p) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:1 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select c from c where c = p limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + Filter: (c.dbid = p.p) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:1 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Result + Output: p.p + -> Materialize + Output: p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select p from p where p = c) from c; + QUERY PLAN +-------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Result + Output: p.p + Filter: (p.p = c.dbid) + -> Materialize + Output: p.p, p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select p from p where p = c limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Result + Output: p.p + Filter: (p.p = c.dbid) + -> Materialize + Output: p.p, p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select c from c limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + -> Materialize + Output: c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select c from c where c = r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Result + Output: c.dbid + Filter: (c.dbid = r.r) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select c from c where c = r limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: c.dbid + -> Result + Output: c.dbid + Filter: (c.dbid = r.r) + -> Materialize + Output: c.dbid, c.dbid + -> Broadcast Motion 1:3 (slice1) + Output: c.dbid, c.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration c + Output: c.dbid, c.dbid + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(17 rows) + +explain (verbose, costs off) +select (select r from r where r = c) from c; + QUERY PLAN +-------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Result + Output: r.r + Filter: (r.r = c.dbid) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select r from r where r = c limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = c.dbid) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select d from d where d = p) from p; + QUERY PLAN +-------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Result + Output: d.d + Filter: (d.d = p.p) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select d from d where d = p limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = p.p) + -> Materialize + Output: d.d, d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: p.p + -> Result + Output: p.p + -> Materialize + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select p from p where p = d) from d; + QUERY PLAN +------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Result + Output: p.p + Filter: (p.p = d.d) + -> Materialize + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select p from p where p = d limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: p.p + -> Result + Output: p.p + Filter: (p.p = d.d) + -> Materialize + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + -> Materialize + Output: d.d + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select d from d where d = r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Result + Output: d.d + Filter: (d.d = r.r) + -> Materialize + Output: d.d, d.d + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select d from d where d = r limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: d.d + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = r.r) + -> Materialize + Output: d.d, d.d + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: d.d, d.d + -> Seq Scan on subselect_gp.d + Output: d.d, d.d + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + -> Materialize + Output: r.r + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select r from r where r = d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Result + Output: r.r + Filter: (r.r = d.d) + -> Materialize + Output: r.r, r.r + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select r from r where r = d limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = d.d) + -> Materialize + Output: r.r, r.r + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: p.p + -> Result + Output: p.p + -> Materialize + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select p from p where p = r) from r; + QUERY PLAN +------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Result + Output: p.p + Filter: (p.p = r.r) + -> Materialize + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select p from p where p = r limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: p.p + -> Result + Output: p.p + Filter: (p.p = r.r) + -> Materialize + Output: p.p, p.p + -> Seq Scan on subselect_gp.p + Output: p.p, p.p + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select r from r where r = p) from p; + QUERY PLAN +-------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Result + Output: r.r + Filter: (r.r = p.p) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(16 rows) + +explain (verbose, costs off) +select (select r from r where r = p limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: r.r + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = p.p) + -> Materialize + Output: r.r, r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r, r.r + -> Seq Scan on subselect_gp.r + Output: r.r, r.r + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +create function i(i int) returns setof int language plpgsql immutable as $$ begin return query select i; end $$; +create function s(s int) returns setof int language plpgsql stable as $$ begin return query select s; end $$; +create function v(v int) returns setof int language plpgsql volatile as $$ begin return query select v; end $$; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; + QUERY PLAN +----------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(d.d) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(d.d) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:3 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:3 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; + QUERY PLAN +----------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(p.p) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(p.p) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:1 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:1 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; + QUERY PLAN +----------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(r.r) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(r.r) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(12 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:3 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:3 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +alter function i(i int) execute on all segments; +alter function s(s int) execute on all segments; +alter function v(v int) execute on all segments; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Gather Motion 3:1 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Gather Motion 3:1 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Gather Motion 3:1 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Gather Motion 3:1 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Gather Motion 3:1 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Gather Motion 3:1 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(19 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Gather Motion 3:1 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Gather Motion 3:1 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Gather Motion 3:1 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(20 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +alter function i(i int) execute on master; +alter function s(s int) execute on master; +alter function v(v int) execute on master; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(8 rows) + +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((c.dbid)::integer) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(10 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 1:3 (slice1) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:3 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:3 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(9 rows) + +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(g.g) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(11 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 1:1 (slice1) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:1 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:1 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 1:3 (slice1) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:3 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:3 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(18 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +alter function i(i int) execute on initplan; +alter function s(s int) execute on initplan; +alter function v(v int) execute on initplan; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 2) + SubPlan 2 + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 2) + SubPlan 2 + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 2) + SubPlan 2 + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(14 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Result + Output: i_1.i + -> Materialize + Output: i_1.i + -> Broadcast Motion 1:3 (slice1) + Output: i_1.i + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Result + Output: s_1.s + -> Materialize + Output: s_1.s + -> Broadcast Motion 1:3 (slice1) + Output: s_1.s + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Result + Output: v_1.v + -> Materialize + Output: v_1.v + -> Broadcast Motion 1:3 (slice1) + Output: v_1.v + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 2) + Function Call: generate_series(0, 9) + SubPlan 2 + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 2) + Function Call: generate_series(0, 9) + SubPlan 2 + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 2) + Function Call: generate_series(0, 9) + SubPlan 2 + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(15 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 1) + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Result + Output: i_1.i + -> Materialize + Output: i_1.i + -> Broadcast Motion 1:1 (slice1) + Output: i_1.i + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 1) + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Result + Output: s_1.s + -> Materialize + Output: s_1.s + -> Broadcast Motion 1:1 (slice1) + Output: s_1.s + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 1) + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Result + Output: v_1.v + -> Materialize + Output: v_1.v + -> Broadcast Motion 1:1 (slice1) + Output: v_1.v + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Result + Output: i_1.i + -> Materialize + Output: i_1.i + -> Broadcast Motion 1:3 (slice1) + Output: i_1.i + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Result + Output: s_1.s + -> Materialize + Output: s_1.s + -> Broadcast Motion 1:3 (slice1) + Output: s_1.s + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Result + Output: v_1.v + -> Materialize + Output: v_1.v + -> Broadcast Motion 1:3 (slice1) + Output: v_1.v + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer + Settings: optimizer=off +(22 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: plan should not reference subplan's variable +drop function i(a int); +drop function s(a int); +drop function v(a int); +drop table d; +drop table p; +drop table r; +drop view c; +drop view g; diff --git a/src/test/regress/expected/subselect_gp_optimizer.out b/src/test/regress/expected/subselect_gp_optimizer.out index 3f7c04fabec2..e0f7f00bad27 100644 --- a/src/test/regress/expected/subselect_gp_optimizer.out +++ b/src/test/regress/expected/subselect_gp_optimizer.out @@ -1,3 +1,7 @@ +-- start_matchsubs +-- m/ \(subselect\.c:\d+\)/ +-- s/ \(subselect\.c:\d+\)// +-- end_matchsubs set optimizer_enable_master_only_queries = on; set optimizer_segments = 3; set optimizer_nestloop_factor = 1.0; @@ -3981,3 +3985,3534 @@ drop table t3; drop table t_repl; drop table t2; drop table t1; +-- Test subplan with correlated functions or master-only tables +create table d(d int) distributed by (d); +insert into d select g from generate_series(0, 9) g; +analyze d; +create table p(p int) distributed replicated; +insert into p select g from generate_series(0, 9) g; +analyze p; +create table r(r int) distributed randomly; +insert into r select g from generate_series(0, 9) g; +analyze r; +create view g as select g from generate_series(0, 9) g; +create view c as select dbid c from gp_segment_configuration c; +explain (verbose, costs off) +select (select c from c limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: gp_segment_configuration.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select c from c where c = g) from g; + QUERY PLAN +------------------------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Filter: (gp_segment_configuration.dbid = generate_series.generate_series) + Optimizer: Pivotal Optimizer (GPORCA) +(10 rows) + +explain (verbose, costs off) +select (select c from c where c = g limit 1 offset g) from g; + QUERY PLAN +------------------------------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: gp_segment_configuration.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Filter: (gp_segment_configuration.dbid = generate_series.generate_series) + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Limit + Output: generate_series.generate_series + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select g from g where g = c) from c; + QUERY PLAN +------------------------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Result + Output: generate_series.generate_series + Filter: (generate_series.generate_series = gp_segment_configuration.dbid) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +explain (verbose, costs off) +select (select g from g where g = c limit 1 offset c) from c; + QUERY PLAN +------------------------------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Limit + Output: generate_series.generate_series + -> Result + Output: generate_series.generate_series + Filter: (generate_series.generate_series = gp_segment_configuration.dbid) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(14 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(15 rows) + +explain (verbose, costs off) +select (select d from d where d = g) from g; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Result + Output: d.d + Filter: (d.d = generate_series.generate_series) + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select d from d where d = g limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = generate_series.generate_series) + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(18 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: generate_series.generate_series + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select g from g where g = d) from d; + QUERY PLAN +----------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice1; segments: 3) + -> Result + Output: generate_series.generate_series + Filter: (generate_series.generate_series = d.d) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(14 rows) + +explain (verbose, costs off) +select (select g from g where g = d limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: generate_series.generate_series + -> Result + Output: generate_series.generate_series + Filter: (generate_series.generate_series = d.d) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Materialize + Output: p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Pivotal Optimizer (GPORCA) +(15 rows) + +explain (verbose, costs off) +select (select p from p where p = g) from g; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Result + Output: p.p + Filter: (p.p = generate_series.generate_series) + -> Materialize + Output: p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select p from p where p = g limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Result + Output: p.p + Filter: (p.p = generate_series.generate_series) + -> Materialize + Output: p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Pivotal Optimizer (GPORCA) +(18 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: generate_series.generate_series + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select g from g where g = p) from p; + QUERY PLAN +----------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice1; segments: 1) + -> Result + Output: generate_series.generate_series + Filter: (generate_series.generate_series = p.p) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(14 rows) + +explain (verbose, costs off) +select (select g from g where g = p limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: generate_series.generate_series + -> Result + Output: generate_series.generate_series + Filter: (generate_series.generate_series = p.p) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(15 rows) + +explain (verbose, costs off) +select (select r from r where r = g) from g; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Result + Output: r.r + Filter: (r.r = generate_series.generate_series) + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select r from r where r = g limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = generate_series.generate_series) + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(18 rows) + +explain (verbose, costs off) +select (select g from g limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: generate_series.generate_series + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select g from g where g = r) from r; + QUERY PLAN +----------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice1; segments: 3) + -> Result + Output: generate_series.generate_series + Filter: (generate_series.generate_series = r.r) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(14 rows) + +explain (verbose, costs off) +select (select g from g where g = r limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: generate_series.generate_series + -> Result + Output: generate_series.generate_series + Filter: (generate_series.generate_series = r.r) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select c from c limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: gp_segment_configuration.dbid + -> Materialize + Output: gp_segment_configuration.dbid + -> Broadcast Motion 1:3 (slice1) + Output: gp_segment_configuration.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select c from c where c = d) from d; + QUERY PLAN +--------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice2; segments: 3) + -> Result + Output: gp_segment_configuration.dbid + Filter: (gp_segment_configuration.dbid = d.d) + -> Materialize + Output: gp_segment_configuration.dbid + -> Broadcast Motion 1:3 (slice1) + Output: gp_segment_configuration.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Optimizer: Pivotal Optimizer (GPORCA) +(17 rows) + +explain (verbose, costs off) +select (select c from c where c = d limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: gp_segment_configuration.dbid + -> Result + Output: gp_segment_configuration.dbid + Filter: (gp_segment_configuration.dbid = d.d) + -> Materialize + Output: gp_segment_configuration.dbid + -> Broadcast Motion 1:3 (slice1) + Output: gp_segment_configuration.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Optimizer: Pivotal Optimizer (GPORCA) +(19 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(14 rows) + +explain (verbose, costs off) +select (select d from d where d = c) from c; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 (slice0) + -> Result + Output: d.d + Filter: (d.d = gp_segment_configuration.dbid) + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(15 rows) + +explain (verbose, costs off) +select (select d from d where d = c limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = gp_segment_configuration.dbid) + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(17 rows) + +explain (verbose, costs off) +select (select c from c limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Limit + Output: gp_segment_configuration.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Optimizer: Pivotal Optimizer (GPORCA) + Settings: optimizer=on +(13 rows) + +explain (verbose, costs off) +select (select c from c where c = p) from p; + QUERY PLAN +--------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Filter: (gp_segment_configuration.dbid = p.p) + Optimizer: Pivotal Optimizer (GPORCA) + Settings: optimizer=on +(12 rows) + +explain (verbose, costs off) +select (select c from c where c = p limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Limit + Output: gp_segment_configuration.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Filter: (gp_segment_configuration.dbid = p.p) + Optimizer: Pivotal Optimizer (GPORCA) + Settings: optimizer=on +(14 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Materialize + Output: p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Pivotal Optimizer (GPORCA) +(14 rows) + +explain (verbose, costs off) +select (select p from p where p = c) from c; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 (slice0) + -> Result + Output: p.p + Filter: (p.p = gp_segment_configuration.dbid) + -> Materialize + Output: p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Pivotal Optimizer (GPORCA) +(15 rows) + +explain (verbose, costs off) +select (select p from p where p = c limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 (slice0) + -> Limit + Output: p.p + -> Result + Output: p.p + Filter: (p.p = gp_segment_configuration.dbid) + -> Materialize + Output: p.p + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Pivotal Optimizer (GPORCA) +(17 rows) + +explain (verbose, costs off) +select (select c from c limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: gp_segment_configuration.dbid + -> Materialize + Output: gp_segment_configuration.dbid + -> Broadcast Motion 1:3 (slice1) + Output: gp_segment_configuration.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select c from c where c = r) from r; + QUERY PLAN +--------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice2; segments: 3) + -> Result + Output: gp_segment_configuration.dbid + Filter: (gp_segment_configuration.dbid = r.r) + -> Materialize + Output: gp_segment_configuration.dbid + -> Broadcast Motion 1:3 (slice1) + Output: gp_segment_configuration.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Optimizer: Pivotal Optimizer (GPORCA) +(17 rows) + +explain (verbose, costs off) +select (select c from c where c = r limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: gp_segment_configuration.dbid + -> Result + Output: gp_segment_configuration.dbid + Filter: (gp_segment_configuration.dbid = r.r) + -> Materialize + Output: gp_segment_configuration.dbid + -> Broadcast Motion 1:3 (slice1) + Output: gp_segment_configuration.dbid + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + Optimizer: Pivotal Optimizer (GPORCA) +(19 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(14 rows) + +explain (verbose, costs off) +select (select r from r where r = c) from c; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 (slice0) + -> Result + Output: r.r + Filter: (r.r = gp_segment_configuration.dbid) + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(15 rows) + +explain (verbose, costs off) +select (select r from r where r = c limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = gp_segment_configuration.dbid) + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(17 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice2; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select d from d where d = p) from p; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Result + Output: d.d + Filter: (d.d = p.p) + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice2; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(17 rows) + +explain (verbose, costs off) +select (select d from d where d = p limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = p.p) + -> Materialize + Output: d.d + -> Gather Motion 3:1 (slice2; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(19 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +explain (verbose, costs off) +select (select p from p where p = d) from d; + QUERY PLAN +------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice1; segments: 3) + -> Seq Scan on subselect_gp.p + Output: p.p + Filter: (p.p = d.d) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select p from p where p = d limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Filter: (p.p = d.d) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select d from d limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: d.d + -> Materialize + Output: d.d + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select d from d where d = r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice2; segments: 3) + -> Result + Output: d.d + Filter: (d.d = r.r) + -> Materialize + Output: d.d + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(17 rows) + +explain (verbose, costs off) +select (select d from d where d = r limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: d.d + -> Result + Output: d.d + Filter: (d.d = r.r) + -> Materialize + Output: d.d + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + Optimizer: Pivotal Optimizer (GPORCA) +(19 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: r.r + -> Materialize + Output: r.r + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select r from r where r = d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice2; segments: 3) + -> Result + Output: r.r + Filter: (r.r = d.d) + -> Materialize + Output: r.r + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(17 rows) + +explain (verbose, costs off) +select (select r from r where r = d limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = d.d) + -> Materialize + Output: r.r + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(19 rows) + +explain (verbose, costs off) +select (select p from p limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +explain (verbose, costs off) +select (select p from p where p = r) from r; + QUERY PLAN +------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice1; segments: 3) + -> Seq Scan on subselect_gp.p + Output: p.p + Filter: (p.p = r.r) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select p from p where p = r limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + Filter: (p.p = r.r) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select r from r limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice2; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) + +explain (verbose, costs off) +select (select r from r where r = p) from p; + QUERY PLAN +-------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Result + Output: r.r + Filter: (r.r = p.p) + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice2; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(17 rows) + +explain (verbose, costs off) +select (select r from r where r = p limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Limit + Output: r.r + -> Result + Output: r.r + Filter: (r.r = p.p) + -> Materialize + Output: r.r + -> Gather Motion 3:1 (slice2; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + Optimizer: Pivotal Optimizer (GPORCA) +(19 rows) + +create function i(i int) returns setof int language plpgsql immutable as $$ begin return query select i; end $$; +create function s(s int) returns setof int language plpgsql stable as $$ begin return query select s; end $$; +create function v(v int) returns setof int language plpgsql volatile as $$ begin return query select v; end $$; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; + QUERY PLAN +---------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((gp_segment_configuration.dbid)::integer) + Optimizer: Pivotal Optimizer (GPORCA) +(9 rows) + +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; + QUERY PLAN +---------------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((gp_segment_configuration.dbid)::integer) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; + QUERY PLAN +---------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((gp_segment_configuration.dbid)::integer) + Optimizer: Pivotal Optimizer (GPORCA) +(9 rows) + +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; + QUERY PLAN +---------------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((gp_segment_configuration.dbid)::integer) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; + QUERY PLAN +---------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((gp_segment_configuration.dbid)::integer) + Optimizer: Pivotal Optimizer (GPORCA) +(9 rows) + +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; + QUERY PLAN +---------------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Seq Scan on pg_catalog.gp_segment_configuration + Output: gp_segment_configuration.dbid + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((gp_segment_configuration.dbid)::integer) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; + QUERY PLAN +----------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice1; segments: 3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(d.d) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(d.d) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice0) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(d.d) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(d.d) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice0) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(d.d) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: d.d + -> Seq Scan on subselect_gp.d + Output: d.d + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(d.d) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; + QUERY PLAN +------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(generate_series.generate_series) + Optimizer: Pivotal Optimizer (GPORCA) +(10 rows) + +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; + QUERY PLAN +------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(generate_series.generate_series) + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; + QUERY PLAN +------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(generate_series.generate_series) + Optimizer: Pivotal Optimizer (GPORCA) +(10 rows) + +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; + QUERY PLAN +------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(generate_series.generate_series) + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; + QUERY PLAN +------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(generate_series.generate_series) + Optimizer: Pivotal Optimizer (GPORCA) +(10 rows) + +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; + QUERY PLAN +------------------------------------------------------------------- + Result + Output: (SubPlan 1) + -> Function Scan on pg_catalog.generate_series + Output: generate_series.generate_series + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(generate_series.generate_series) + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; + QUERY PLAN +----------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice1; segments: 1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(p.p) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------- + Gather Motion 1:1 (slice1; segments: 1) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice1; segments: 1) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(p.p) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(p.p) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(p.p) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(p.p) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 1:1 (slice1; segments: 1) + Output: p.p + -> Seq Scan on subselect_gp.p + Output: p.p + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(p.p) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; + QUERY PLAN +----------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice1; segments: 3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(r.r) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((SubPlan 1)) + -> Result + Output: (SubPlan 1) + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice1; segments: 3) + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(r.r) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice0) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(r.r) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(r.r) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice0) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(r.r) + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) + +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; + QUERY PLAN +------------------------------------------------ + Result + Output: (SubPlan 1) + -> Gather Motion 3:1 (slice1; segments: 3) + Output: r.r + -> Seq Scan on subselect_gp.r + Output: r.r + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(r.r) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) + +alter function i(i int) execute on all segments; +alter function s(s int) execute on all segments; +alter function v(v int) execute on all segments; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Gather Motion 3:1 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Gather Motion 3:1 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +-------------------------------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Gather Motion 3:1 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(19 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(19 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(19 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Gather Motion 3:1 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(18 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Gather Motion 3:1 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(18 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +-------------------------------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 (slice0) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Gather Motion 3:1 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(18 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Gather Motion 3:1 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(19 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Gather Motion 3:1 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(19 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +-------------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Gather Motion 3:1 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(19 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(19 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(19 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(19 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +alter function i(i int) execute on master; +alter function s(s int) execute on master; +alter function v(v int) execute on master; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(9 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((c.dbid)::integer) + Optimizer: Postgres query optimizer +(7 rows) + +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i((c.dbid)::integer) + Optimizer: Postgres query optimizer +(9 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(9 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((c.dbid)::integer) + Optimizer: Postgres query optimizer +(7 rows) + +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s((c.dbid)::integer) + Optimizer: Postgres query optimizer +(9 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(9 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((c.dbid)::integer) + Optimizer: Postgres query optimizer +(7 rows) + +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; + QUERY PLAN +----------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 1) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v((c.dbid)::integer) + Optimizer: Postgres query optimizer +(9 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 1:3 (slice1) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:3 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:3 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(10 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(g.g) + Optimizer: Postgres query optimizer +(8 rows) + +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(g.g) + Optimizer: Postgres query optimizer +(10 rows) + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(10 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(g.g) + Optimizer: Postgres query optimizer +(8 rows) + +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(g.g) + Optimizer: Postgres query optimizer +(10 rows) + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(10 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(g.g) + Optimizer: Postgres query optimizer +(8 rows) + +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; + QUERY PLAN +----------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 1) + Function Call: generate_series(0, 9) + SubPlan 1 + -> Limit + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(g.g) + Optimizer: Postgres query optimizer +(10 rows) + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 1:1 (slice1) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:1 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 1) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:1 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: i.i + -> Result + Output: i.i + -> Materialize + Output: i.i + -> Broadcast Motion 1:3 (slice1) + Output: i.i + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: s.s + -> Result + Output: s.s + -> Materialize + Output: s.s + -> Broadcast Motion 1:3 (slice1) + Output: s.s + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +----------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 1)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 1) + SubPlan 1 (slice2; segments: 3) + -> Limit + Output: v.v + -> Result + Output: v.v + -> Materialize + Output: v.v + -> Broadcast Motion 1:3 (slice1) + Output: v.v + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(17 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: cannot materialize function with correlated parameters +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: cannot materialize function with correlated parameters +alter function i(i int) execute on initplan; +alter function s(s int) execute on initplan; +alter function v(v int) execute on initplan; +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 2) + SubPlan 2 + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(13 rows) + +explain (verbose, costs off) +select (select i from i(c)) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 2) + SubPlan 2 + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(13 rows) + +explain (verbose, costs off) +select (select s from s(c)) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; + QUERY PLAN +--------------------------------------------------- + Seq Scan on pg_catalog.gp_segment_configuration c + Output: (SubPlan 2) + SubPlan 2 + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(13 rows) + +explain (verbose, costs off) +select (select v from v(c)) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Result + Output: i_1.i + -> Materialize + Output: i_1.i + -> Broadcast Motion 1:3 (slice1) + Output: i_1.i + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(21 rows) + +explain (verbose, costs off) +select (select i from i(d)) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Result + Output: s_1.s + -> Materialize + Output: s_1.s + -> Broadcast Motion 1:3 (slice1) + Output: s_1.s + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(21 rows) + +explain (verbose, costs off) +select (select s from s(d)) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.d + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Result + Output: v_1.v + -> Materialize + Output: v_1.v + -> Broadcast Motion 1:3 (slice1) + Output: v_1.v + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(21 rows) + +explain (verbose, costs off) +select (select v from v(d)) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 2) + Function Call: generate_series(0, 9) + SubPlan 2 + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(14 rows) + +explain (verbose, costs off) +select (select i from i(g)) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 2) + Function Call: generate_series(0, 9) + SubPlan 2 + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(14 rows) + +explain (verbose, costs off) +select (select s from s(g)) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; + QUERY PLAN +--------------------------------------------------- + Function Scan on pg_catalog.generate_series g + Output: (SubPlan 2) + Function Call: generate_series(0, 9) + SubPlan 2 + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(14 rows) + +explain (verbose, costs off) +select (select v from v(g)) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 1) + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Result + Output: i_1.i + -> Materialize + Output: i_1.i + -> Broadcast Motion 1:1 (slice1) + Output: i_1.i + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(21 rows) + +explain (verbose, costs off) +select (select i from i(p)) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 1) + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Result + Output: s_1.s + -> Materialize + Output: s_1.s + -> Broadcast Motion 1:1 (slice1) + Output: s_1.s + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(21 rows) + +explain (verbose, costs off) +select (select s from s(p)) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 1:1 (slice2; segments: 1) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.p + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 1) + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Result + Output: v_1.v + -> Materialize + Output: v_1.v + -> Broadcast Motion 1:1 (slice1) + Output: v_1.v + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(21 rows) + +explain (verbose, costs off) +select (select v from v(p)) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: i_1.i + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.i + Output: i.i + Function Call: i(1) + -> Result + Output: i_1.i + -> Materialize + Output: i_1.i + -> Broadcast Motion 1:3 (slice1) + Output: i_1.i + -> Function Scan on subselect_gp.i i_1 + Output: i_1.i + Function Call: i(1) + Optimizer: Postgres query optimizer +(21 rows) + +explain (verbose, costs off) +select (select i from i(r)) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: s_1.s + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.s + Output: s.s + Function Call: s(1) + -> Result + Output: s_1.s + -> Materialize + Output: s_1.s + -> Broadcast Motion 1:3 (slice1) + Output: s_1.s + -> Function Scan on subselect_gp.s s_1 + Output: s_1.s + Function Call: s(1) + Optimizer: Postgres query optimizer +(21 rows) + +explain (verbose, costs off) +select (select s from s(r)) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; + QUERY PLAN +--------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: ((SubPlan 2)) + -> Seq Scan on subselect_gp.r + Output: (SubPlan 2) + SubPlan 2 (slice2; segments: 3) + -> Limit + Output: v_1.v + InitPlan 1 (returns $1) (slice3) + -> Function Scan on subselect_gp.v + Output: v.v + Function Call: v(1) + -> Result + Output: v_1.v + -> Materialize + Output: v_1.v + -> Broadcast Motion 1:3 (slice1) + Output: v_1.v + -> Function Scan on subselect_gp.v v_1 + Output: v_1.v + Function Call: v(1) + Optimizer: Postgres query optimizer +(21 rows) + +explain (verbose, costs off) +select (select v from v(r)) from r; +ERROR: plan should not reference subplan's variable +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; +ERROR: plan should not reference subplan's variable +drop function i(a int); +drop function s(a int); +drop function v(a int); +drop table d; +drop table p; +drop table r; +drop view c; +drop view g; diff --git a/src/test/regress/sql/rangefuncs_cdb.sql b/src/test/regress/sql/rangefuncs_cdb.sql index fb37e3561154..816522143574 100644 --- a/src/test/regress/sql/rangefuncs_cdb.sql +++ b/src/test/regress/sql/rangefuncs_cdb.sql @@ -38,7 +38,7 @@ BEGIN RETURN NEXT r; RETURN; END -$$ language plpgsql; +$$ language plpgsql IMMUTABLE; -- function in select clause select foost(1); @@ -101,7 +101,7 @@ BEGIN RETURN NEXT rec; return; END -$$ LANGUAGE plpgsql; +$$ LANGUAGE plpgsql IMMUTABLE; -- function in select clause -- Fails: plpgsql does not support SFRM_Materialize @@ -173,7 +173,7 @@ BEGIN RETURN NEXT; RETURN; END; -$$ LANGUAGE plpgsql; +$$ LANGUAGE plpgsql IMMUTABLE; -- function in select clause select fooro(1); @@ -231,7 +231,7 @@ BEGIN RETURN NEXT; RETURN; END -$$ language plpgsql; +$$ language plpgsql IMMUTABLE; -- function in select clause select foot(1); diff --git a/src/test/regress/sql/subselect_gp.sql b/src/test/regress/sql/subselect_gp.sql index 1209cb03f0d6..ec03a833983d 100644 --- a/src/test/regress/sql/subselect_gp.sql +++ b/src/test/regress/sql/subselect_gp.sql @@ -1,3 +1,7 @@ +-- start_matchsubs +-- m/ \(subselect\.c:\d+\)/ +-- s/ \(subselect\.c:\d+\)// +-- end_matchsubs -- start_ignore create schema subselect_gp; set search_path to subselect_gp, public; @@ -1482,3 +1486,640 @@ drop table t3; drop table t_repl; drop table t2; drop table t1; + +-- Test subplan with correlated functions or master-only tables +--start_ignore +drop function if exists i(a int); +drop function if exists s(a int); +drop function if exists v(a int); +drop table if exists d; +drop table if exists p; +drop table if exists r; +drop view if exists c; +drop view if exists g; +--end_ignore +create table d(d int) distributed by (d); +insert into d select g from generate_series(0, 9) g; +analyze d; + +create table p(p int) distributed replicated; +insert into p select g from generate_series(0, 9) g; +analyze p; + +create table r(r int) distributed randomly; +insert into r select g from generate_series(0, 9) g; +analyze r; + +create view g as select g from generate_series(0, 9) g; +create view c as select dbid c from gp_segment_configuration c; + + +explain (verbose, costs off) +select (select c from c limit 1 offset g) from g; +explain (verbose, costs off) +select (select c from c where c = g) from g; +explain (verbose, costs off) +select (select c from c where c = g limit 1 offset g) from g; + +explain (verbose, costs off) +select (select g from g limit 1 offset c) from c; +explain (verbose, costs off) +select (select g from g where g = c) from c; +explain (verbose, costs off) +select (select g from g where g = c limit 1 offset c) from c; + +explain (verbose, costs off) +select (select d from d limit 1 offset g) from g; +explain (verbose, costs off) +select (select d from d where d = g) from g; +explain (verbose, costs off) +select (select d from d where d = g limit 1 offset g) from g; + +explain (verbose, costs off) +select (select g from g limit 1 offset d) from d; +explain (verbose, costs off) +select (select g from g where g = d) from d; +explain (verbose, costs off) +select (select g from g where g = d limit 1 offset d) from d; + +explain (verbose, costs off) +select (select p from p limit 1 offset g) from g; +explain (verbose, costs off) +select (select p from p where p = g) from g; +explain (verbose, costs off) +select (select p from p where p = g limit 1 offset g) from g; + +explain (verbose, costs off) +select (select g from g limit 1 offset p) from p; +explain (verbose, costs off) +select (select g from g where g = p) from p; +explain (verbose, costs off) +select (select g from g where g = p limit 1 offset p) from p; + +explain (verbose, costs off) +select (select r from r limit 1 offset g) from g; +explain (verbose, costs off) +select (select r from r where r = g) from g; +explain (verbose, costs off) +select (select r from r where r = g limit 1 offset g) from g; + +explain (verbose, costs off) +select (select g from g limit 1 offset r) from r; +explain (verbose, costs off) +select (select g from g where g = r) from r; +explain (verbose, costs off) +select (select g from g where g = r limit 1 offset r) from r; + +explain (verbose, costs off) +select (select c from c limit 1 offset d) from d; +explain (verbose, costs off) +select (select c from c where c = d) from d; +explain (verbose, costs off) +select (select c from c where c = d limit 1 offset d) from d; + +explain (verbose, costs off) +select (select d from d limit 1 offset c) from c; +explain (verbose, costs off) +select (select d from d where d = c) from c; +explain (verbose, costs off) +select (select d from d where d = c limit 1 offset c) from c; + +explain (verbose, costs off) +select (select c from c limit 1 offset p) from p; +explain (verbose, costs off) +select (select c from c where c = p) from p; +explain (verbose, costs off) +select (select c from c where c = p limit 1 offset p) from p; + +explain (verbose, costs off) +select (select p from p limit 1 offset c) from c; +explain (verbose, costs off) +select (select p from p where p = c) from c; +explain (verbose, costs off) +select (select p from p where p = c limit 1 offset c) from c; + +explain (verbose, costs off) +select (select c from c limit 1 offset r) from r; +explain (verbose, costs off) +select (select c from c where c = r) from r; +explain (verbose, costs off) +select (select c from c where c = r limit 1 offset r) from r; + +explain (verbose, costs off) +select (select r from r limit 1 offset c) from c; +explain (verbose, costs off) +select (select r from r where r = c) from c; +explain (verbose, costs off) +select (select r from r where r = c limit 1 offset c) from c; + +explain (verbose, costs off) +select (select d from d limit 1 offset p) from p; +explain (verbose, costs off) +select (select d from d where d = p) from p; +explain (verbose, costs off) +select (select d from d where d = p limit 1 offset p) from p; + +explain (verbose, costs off) +select (select p from p limit 1 offset d) from d; +explain (verbose, costs off) +select (select p from p where p = d) from d; +explain (verbose, costs off) +select (select p from p where p = d limit 1 offset d) from d; + +explain (verbose, costs off) +select (select d from d limit 1 offset r) from r; +explain (verbose, costs off) +select (select d from d where d = r) from r; +explain (verbose, costs off) +select (select d from d where d = r limit 1 offset r) from r; + +explain (verbose, costs off) +select (select r from r limit 1 offset d) from d; +explain (verbose, costs off) +select (select r from r where r = d) from d; +explain (verbose, costs off) +select (select r from r where r = d limit 1 offset d) from d; + +explain (verbose, costs off) +select (select p from p limit 1 offset r) from r; +explain (verbose, costs off) +select (select p from p where p = r) from r; +explain (verbose, costs off) +select (select p from p where p = r limit 1 offset r) from r; + +explain (verbose, costs off) +select (select r from r limit 1 offset p) from p; +explain (verbose, costs off) +select (select r from r where r = p) from p; +explain (verbose, costs off) +select (select r from r where r = p limit 1 offset p) from p; + + +create function i(i int) returns setof int language plpgsql immutable as $$ begin return query select i; end $$; +create function s(s int) returns setof int language plpgsql stable as $$ begin return query select s; end $$; +create function v(v int) returns setof int language plpgsql volatile as $$ begin return query select v; end $$; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select i from i(c)) from c; +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select s from s(c)) from c; +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select v from v(c)) from c; +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select i from i(d)) from d; +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select s from s(d)) from d; +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select v from v(d)) from d; +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select i from i(g)) from g; +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select s from s(g)) from g; +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select v from v(g)) from g; +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select i from i(p)) from p; +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select s from s(p)) from p; +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select v from v(p)) from p; +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select i from i(r)) from r; +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select s from s(r)) from r; +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select v from v(r)) from r; +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; + + +alter function i(i int) execute on all segments; +alter function s(s int) execute on all segments; +alter function v(v int) execute on all segments; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select i from i(c)) from c; +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select s from s(c)) from c; +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select v from v(c)) from c; +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select i from i(d)) from d; +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select s from s(d)) from d; +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select v from v(d)) from d; +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select i from i(g)) from g; +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select s from s(g)) from g; +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select v from v(g)) from g; +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select i from i(p)) from p; +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select s from s(p)) from p; +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select v from v(p)) from p; +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select i from i(r)) from r; +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select s from s(r)) from r; +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select v from v(r)) from r; +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; + + +alter function i(i int) execute on master; +alter function s(s int) execute on master; +alter function v(v int) execute on master; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select i from i(c)) from c; +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select s from s(c)) from c; +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select v from v(c)) from c; +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select i from i(d)) from d; +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select s from s(d)) from d; +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select v from v(d)) from d; +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select i from i(g)) from g; +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select s from s(g)) from g; +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select v from v(g)) from g; +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select i from i(p)) from p; +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select s from s(p)) from p; +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select v from v(p)) from p; +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select i from i(r)) from r; +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select s from s(r)) from r; +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select v from v(r)) from r; +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; + + +alter function i(i int) execute on initplan; +alter function s(s int) execute on initplan; +alter function v(v int) execute on initplan; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select i from i(c)) from c; +explain (verbose, costs off) +select (select i from i(c) limit 1 offset c) from c; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select s from s(c)) from c; +explain (verbose, costs off) +select (select s from s(c) limit 1 offset c) from c; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset c) from c; +explain (verbose, costs off) +select (select v from v(c)) from c; +explain (verbose, costs off) +select (select v from v(c) limit 1 offset c) from c; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select i from i(d)) from d; +explain (verbose, costs off) +select (select i from i(d) limit 1 offset d) from d; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select s from s(d)) from d; +explain (verbose, costs off) +select (select s from s(d) limit 1 offset d) from d; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset d) from d; +explain (verbose, costs off) +select (select v from v(d)) from d; +explain (verbose, costs off) +select (select v from v(d) limit 1 offset d) from d; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select i from i(g)) from g; +explain (verbose, costs off) +select (select i from i(g) limit 1 offset g) from g; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select s from s(g)) from g; +explain (verbose, costs off) +select (select s from s(g) limit 1 offset g) from g; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset g) from g; +explain (verbose, costs off) +select (select v from v(g)) from g; +explain (verbose, costs off) +select (select v from v(g) limit 1 offset g) from g; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select i from i(p)) from p; +explain (verbose, costs off) +select (select i from i(p) limit 1 offset p) from p; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select s from s(p)) from p; +explain (verbose, costs off) +select (select s from s(p) limit 1 offset p) from p; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset p) from p; +explain (verbose, costs off) +select (select v from v(p)) from p; +explain (verbose, costs off) +select (select v from v(p) limit 1 offset p) from p; + + +explain (verbose, costs off) +select (select i from i(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select i from i(r)) from r; +explain (verbose, costs off) +select (select i from i(r) limit 1 offset r) from r; + +explain (verbose, costs off) +select (select s from s(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select s from s(r)) from r; +explain (verbose, costs off) +select (select s from s(r) limit 1 offset r) from r; + +explain (verbose, costs off) +select (select v from v(1) limit 1 offset r) from r; +explain (verbose, costs off) +select (select v from v(r)) from r; +explain (verbose, costs off) +select (select v from v(r) limit 1 offset r) from r; + + +drop function i(a int); +drop function s(a int); +drop function v(a int); +drop table d; +drop table p; +drop table r; +drop view c; +drop view g; From b5f63c6571e4300187a1d88e266520d0187fdd82 Mon Sep 17 00:00:00 2001 From: Roman Eskin Date: Tue, 9 Dec 2025 08:17:39 +1000 Subject: [PATCH 031/111] Fix orphaned segment files for added and rolled back columns for AOCS tables (#139) Problem description: In case we added a new column to a AOCS table in a transaction that was later rolled back, a non-empty segment file could be left, and vacuum didn't truncate it. Root cause: Vacuum does truncation of AOCS segment files in 2 places: 1. In AOCSSegmentFileTruncateToEOF(). It iterated over 'vpinfo.nEntry', which doesn't count the added and rolled back attribute, so its segment file was not processed here. 2. In AOCSCompaction_DropSegmentFile() function (if compaction is required for the segment). It iterates over attributes retrieved by RelationGetNumberOfAttributes(). But, the added and rolled back attribute is not counted by RelationGetNumberOfAttributes(), so its segment file was not processed here as well. Fix: In AOCSSegmentFileTruncateToEOF() iterate and truncate all segment files for a given 'segno' until they exist. We stop once we didn't find a file and column number is above 'vpinfo.nEntry'. We assume that the segment files exist or stop existing for all columns thereafter - similar logic we already use in ao_foreach_extent_file(). We do it not in AOCSCompaction_DropSegmentFile(), as it will not be called if compaction is not required, while the orphaned segment files may still exist in this case. --- src/backend/access/aocs/aocs_compaction.c | 21 +++++-- src/test/regress/expected/vacuum_full_ao.out | 48 +++++++++++++-- src/test/regress/sql/vacuum_full_ao.sql | 61 ++++++++++++++++++-- 3 files changed, 117 insertions(+), 13 deletions(-) diff --git a/src/backend/access/aocs/aocs_compaction.c b/src/backend/access/aocs/aocs_compaction.c index e531714c1aa8..e0a1ac998256 100644 --- a/src/backend/access/aocs/aocs_compaction.c +++ b/src/backend/access/aocs/aocs_compaction.c @@ -108,16 +108,24 @@ AOCSSegmentFileTruncateToEOF(Relation aorel, segno = fsinfo->segno; - for (j = 0; j < fsinfo->vpinfo.nEntry; ++j) + /* + * We try to truncate all segment files beyond + * `vpinfo.nEntry`, as we may have non-empty segment files + * left by ADD COLUMN, which was rolled back. It is similar to logic in + * ao_foreach_extent_file(). + */ + for (j = 0; ; ++j) { - int64 segeof; + int64 segeof = 0; char filenamepath[MAXPGPATH]; - AOCSVPInfoEntry *entry; File fd; int32 fileSegNo; - entry = getAOCSVPEntry(fsinfo, j); - segeof = entry->eof; + if (j < fsinfo->vpinfo.nEntry) + { + AOCSVPInfoEntry *entry = getAOCSVPEntry(fsinfo, j); + segeof = entry->eof; + } /* Open and truncate the relation segfile to its eof */ MakeAOSegmentFileName(aorel, segno, j, &fileSegNo, filenamepath); @@ -162,6 +170,9 @@ AOCSSegmentFileTruncateToEOF(Relation aorel, segno, fileSegNo, segeof); + + if (j >= fsinfo->vpinfo.nEntry) + break; } } } diff --git a/src/test/regress/expected/vacuum_full_ao.out b/src/test/regress/expected/vacuum_full_ao.out index b68c24ed2ed3..d4d4210a42a0 100644 --- a/src/test/regress/expected/vacuum_full_ao.out +++ b/src/test/regress/expected/vacuum_full_ao.out @@ -44,13 +44,53 @@ create or replace function getTableSegFiles(t regclass, out gp_contentid smallin as 'select current_setting(''gp_contentid'')::smallint, pg_relation_filepath(t)' language sql execute on all segments; -select '\! (stat --format=''%s'' ' || string_agg(full_path_relfilenode, ' ') || ' 2>/dev/null || echo 0) | awk ''{sum += $1} END {print sum}''' check_segfiles_size from -(select d.datadir || '/' || f.filepath || '.*' as full_path_relfilenode from getTableSegFiles('table_ao_col'::regclass) f join gp_segment_configuration d -on f.gp_contentid = d.content where d.content <> -1 and d.role = 'p')t +create or replace function cmdCheckSegmentFileSizes(table_name text) returns text as +$$ +declare + cmd text; +begin + select '\! (stat --format=''%s'' ' || string_agg(full_path_relfilenode, ' ') || ' 2>/dev/null || echo 0) | awk ''{sum += $1} END {print sum}''' + into cmd + from + (select d.datadir || '/' || f.filepath || '.*' as full_path_relfilenode from getTableSegFiles(table_name::regclass) f join gp_segment_configuration d + on f.gp_contentid = d.content where d.content <> -1 and d.role = 'p')t; + + return cmd; +end +$$ language plpgsql; +select cmdCheckSegmentFileSizes('table_ao_col') check_segfiles_size \gset delete from table_ao_col where true; vacuum table_ao_col; :check_segfiles_size 0 -drop function getTableSegFiles(t regclass, out gp_contentid smallint, out filepath text); drop table table_ao_col; +-- Test that vacuum can process segment files created for a new column in an aborted transaction (case 1) +create table table_ao_col_1 (i int, j int, k int) with (appendonly='true', orientation="column"); +insert into table_ao_col_1 select i, i + 1, i + 2 from generate_series(1, 20) i; +begin; +alter table table_ao_col_1 add column a int; +update table_ao_col_1 set a = 1 where true; +rollback; +select cmdCheckSegmentFileSizes('table_ao_col_1') check_segfiles_size_1 +\gset +delete from table_ao_col_1 where true; +vacuum table_ao_col_1; +:check_segfiles_size_1 +0 +drop table table_ao_col_1; +-- Test that vacuum can process segment files created for a new column in an aborted transaction (case 2) +create table table_ao_col_2 (i int, j int, k int) with (appendonly='true', orientation="column"); +begin; +insert into table_ao_col_2 select i, i + 1, i + 2 from generate_series(1, 20) i; +alter table table_ao_col_2 add column a int; +update table_ao_col_2 set a = 1 where true; +rollback; +select cmdCheckSegmentFileSizes('table_ao_col_2') check_segfiles_size_2 +\gset +vacuum table_ao_col_2; +:check_segfiles_size_2 +0 +drop table table_ao_col_2; +drop function cmdCheckSegmentFileSizes(table_name text); +drop function getTableSegFiles(t regclass, out gp_contentid smallint, out filepath text); diff --git a/src/test/regress/sql/vacuum_full_ao.sql b/src/test/regress/sql/vacuum_full_ao.sql index 240a2f1ad739..73c9672b190d 100644 --- a/src/test/regress/sql/vacuum_full_ao.sql +++ b/src/test/regress/sql/vacuum_full_ao.sql @@ -38,9 +38,22 @@ as 'select current_setting(''gp_contentid'')::smallint, pg_relation_filepath(t)' language sql execute on all segments; -select '\! (stat --format=''%s'' ' || string_agg(full_path_relfilenode, ' ') || ' 2>/dev/null || echo 0) | awk ''{sum += $1} END {print sum}''' check_segfiles_size from -(select d.datadir || '/' || f.filepath || '.*' as full_path_relfilenode from getTableSegFiles('table_ao_col'::regclass) f join gp_segment_configuration d -on f.gp_contentid = d.content where d.content <> -1 and d.role = 'p')t +create or replace function cmdCheckSegmentFileSizes(table_name text) returns text as +$$ +declare + cmd text; +begin + select '\! (stat --format=''%s'' ' || string_agg(full_path_relfilenode, ' ') || ' 2>/dev/null || echo 0) | awk ''{sum += $1} END {print sum}''' + into cmd + from + (select d.datadir || '/' || f.filepath || '.*' as full_path_relfilenode from getTableSegFiles(table_name::regclass) f join gp_segment_configuration d + on f.gp_contentid = d.content where d.content <> -1 and d.role = 'p')t; + + return cmd; +end +$$ language plpgsql; + +select cmdCheckSegmentFileSizes('table_ao_col') check_segfiles_size \gset delete from table_ao_col where true; @@ -49,5 +62,45 @@ vacuum table_ao_col; :check_segfiles_size -drop function getTableSegFiles(t regclass, out gp_contentid smallint, out filepath text); drop table table_ao_col; + +-- Test that vacuum can process segment files created for a new column in an aborted transaction (case 1) +create table table_ao_col_1 (i int, j int, k int) with (appendonly='true', orientation="column"); +insert into table_ao_col_1 select i, i + 1, i + 2 from generate_series(1, 20) i; + +begin; +alter table table_ao_col_1 add column a int; +update table_ao_col_1 set a = 1 where true; +rollback; + +select cmdCheckSegmentFileSizes('table_ao_col_1') check_segfiles_size_1 +\gset + +delete from table_ao_col_1 where true; + +vacuum table_ao_col_1; + +:check_segfiles_size_1 + +drop table table_ao_col_1; + +-- Test that vacuum can process segment files created for a new column in an aborted transaction (case 2) +create table table_ao_col_2 (i int, j int, k int) with (appendonly='true', orientation="column"); + +begin; +insert into table_ao_col_2 select i, i + 1, i + 2 from generate_series(1, 20) i; +alter table table_ao_col_2 add column a int; +update table_ao_col_2 set a = 1 where true; +rollback; + +select cmdCheckSegmentFileSizes('table_ao_col_2') check_segfiles_size_2 +\gset + +vacuum table_ao_col_2; + +:check_segfiles_size_2 + +drop table table_ao_col_2; + +drop function cmdCheckSegmentFileSizes(table_name text); +drop function getTableSegFiles(t regclass, out gp_contentid smallint, out filepath text); From 3c86da9d8c0f1283a0a7776002f08b86fd12ad33 Mon Sep 17 00:00:00 2001 From: Zhang Mingli Date: Tue, 6 Feb 2024 12:55:37 +0800 Subject: [PATCH 032/111] Fix typo of statistics under gporca. A misspell search by mistake find these typos, fix them by the way. Authored-by: Zhang Mingli avamingli@gmail.com (cherry picked from commit 773918993d4374ae809e71b1b6b85ccac25e08a8) --- .../libnaucrates/src/parser/CParseHandlerDynamicTableScan.cpp | 2 +- .../libnaucrates/src/parser/CParseHandlerGatherMotion.cpp | 2 +- .../gporca/libnaucrates/src/parser/CParseHandlerHashJoin.cpp | 2 +- .../gporca/libnaucrates/src/parser/CParseHandlerMaterialize.cpp | 2 +- .../gporca/libnaucrates/src/parser/CParseHandlerMergeJoin.cpp | 2 +- .../gporca/libnaucrates/src/parser/CParseHandlerNLJoin.cpp | 2 +- .../src/parser/CParseHandlerPhysicalAbstractBitmapScan.cpp | 2 +- .../libnaucrates/src/parser/CParseHandlerRandomMotion.cpp | 2 +- .../libnaucrates/src/parser/CParseHandlerRedistributeMotion.cpp | 2 +- .../gporca/libnaucrates/src/parser/CParseHandlerResult.cpp | 2 +- .../libnaucrates/src/parser/CParseHandlerRoutedMotion.cpp | 2 +- .../gporca/libnaucrates/src/parser/CParseHandlerSort.cpp | 2 +- .../libnaucrates/src/parser/CParseHandlerSubqueryScan.cpp | 2 +- .../gporca/libnaucrates/src/parser/CParseHandlerTableScan.cpp | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerDynamicTableScan.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerDynamicTableScan.cpp index d007e52a8104..ea8f217c6dea 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerDynamicTableScan.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerDynamicTableScan.cpp @@ -162,7 +162,7 @@ CParseHandlerDynamicTableScan::EndElement(const XMLCh *const, // element_uri, m_mp, table_descr, m_part_index_id, m_part_index_id_printable); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add constructed children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerGatherMotion.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerGatherMotion.cpp index 56766a48b21a..9f926ce1da28 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerGatherMotion.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerGatherMotion.cpp @@ -160,7 +160,7 @@ CParseHandlerGatherMotion::EndElement(const XMLCh *const, // element_uri, GPOS_ASSERT(NULL != child_parse_handler); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerHashJoin.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerHashJoin.cpp index 5d40be605b97..1260ae54be84 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerHashJoin.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerHashJoin.cpp @@ -180,7 +180,7 @@ CParseHandlerHashJoin::EndElement(const XMLCh *const, // element_uri, GPOS_ASSERT(NULL != right_child_parse_handler); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerMaterialize.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerMaterialize.cpp index de235cce74ad..4ec5c3c606f6 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerMaterialize.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerMaterialize.cpp @@ -150,7 +150,7 @@ CParseHandlerMaterialize::EndElement(const XMLCh *const, // element_uri, GPOS_ASSERT(NULL != child_parse_handler); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add constructed children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerMergeJoin.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerMergeJoin.cpp index 25a1dfec9f2a..ff5b1b08f0dc 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerMergeJoin.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerMergeJoin.cpp @@ -180,7 +180,7 @@ CParseHandlerMergeJoin::EndElement(const XMLCh *const, // element_uri, GPOS_ASSERT(NULL != right_child_parse_handler); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerNLJoin.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerNLJoin.cpp index ec806fd3e27d..2f30fce70ee8 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerNLJoin.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerNLJoin.cpp @@ -194,7 +194,7 @@ CParseHandlerNLJoin::EndElement(const XMLCh *const, // element_uri, m_dxl_op->SetNestLoopParamsColRefs(nest_params_colrefs); } m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add constructed children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalAbstractBitmapScan.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalAbstractBitmapScan.cpp index 90e8ffeb65ab..0011b26980f2 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalAbstractBitmapScan.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalAbstractBitmapScan.cpp @@ -162,7 +162,7 @@ CParseHandlerPhysicalAbstractBitmapScan::EndElementHelper( } m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add constructed children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRandomMotion.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRandomMotion.cpp index 7ce7235acad5..3fe6a23bd395 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRandomMotion.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRandomMotion.cpp @@ -160,7 +160,7 @@ CParseHandlerRandomMotion::EndElement(const XMLCh *const, // element_uri, GPOS_ASSERT(NULL != child_parse_handler); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRedistributeMotion.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRedistributeMotion.cpp index 62b5ec7d13e0..97d8d5b182dc 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRedistributeMotion.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRedistributeMotion.cpp @@ -176,7 +176,7 @@ CParseHandlerRedistributeMotion::EndElement( GPOS_ASSERT(NULL != child_parse_handler); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerResult.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerResult.cpp index 85fa41b1441a..315303710144 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerResult.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerResult.cpp @@ -178,7 +178,7 @@ CParseHandlerResult::EndElement(const XMLCh *const, // element_uri, GPOS_ASSERT(NULL != one_time_filter_parse_handler); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add constructed children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRoutedMotion.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRoutedMotion.cpp index d32abfbb0187..0e3638fa56cd 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRoutedMotion.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerRoutedMotion.cpp @@ -162,7 +162,7 @@ CParseHandlerRoutedMotion::EndElement(const XMLCh *const, // element_uri, GPOS_ASSERT(NULL != child_parse_handler); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerSort.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerSort.cpp index bed106991b3e..5ba4eb31d9c2 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerSort.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerSort.cpp @@ -181,7 +181,7 @@ CParseHandlerSort::EndElement(const XMLCh *const, // element_uri, GPOS_ASSERT(NULL != child_parse_handler); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerSubqueryScan.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerSubqueryScan.cpp index 604c49b1a3fd..1d285dc0b71c 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerSubqueryScan.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerSubqueryScan.cpp @@ -146,7 +146,7 @@ CParseHandlerSubqueryScan::EndElement(const XMLCh *const, // element_uri, GPOS_ASSERT(NULL != child_parse_handler); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add constructed children diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerTableScan.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerTableScan.cpp index f6f0c7123fc7..1280d01755f5 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerTableScan.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerTableScan.cpp @@ -189,7 +189,7 @@ CParseHandlerTableScan::EndElement(const XMLCh *const element_local_name, m_dxl_op->SetTableDescriptor(table_descr); m_dxl_node = GPOS_NEW(m_mp) CDXLNode(m_mp, m_dxl_op); - // set statictics and physical properties + // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); // add constructed children From bc4b8db4cf1326bf1dee2bbcf8b2a64829894614 Mon Sep 17 00:00:00 2001 From: Roman Eskin Date: Thu, 11 Dec 2025 05:30:00 +1000 Subject: [PATCH 033/111] Fix vacuum doesn't compact AO RO table after rolled back transaction (#151) Problem description: In case a column was added to a AO row-oriented table in a transaction (and some values were set to this new column), and this transaction was rolled back, vacuum didn't compact the table. Root cause: In case a new column is added to a AO row-oriented table, the table has to be rewritten, and a new 'pg_aoseg_...' table is created. Segment data from the new segment table is also stored to 'AppendOnlyHash' hash table entry. If we rollback the transaction, 'pg_aoseg_...' table is rolled back to the old one. But the hash entry continued to exist with the values from the transient pg_aoseg table. So, during vacuum we tried to get the list of segments we had to compact and called SetSegnoForCompaction(), which requested data from the hash (via AORelGetOrCreateHashEntry()) and got the hash entry with 0 tupcount. Therefore, compaction didn't happen. Fix: Remove the hash entry from 'AppendOnlyHash' in 'AtEOXact_AppendOnly_Relation()' if the transaction is aborted. It is done for both row and column oriented tables, as both are susceptible to the non-valid hash entry. 'AtEOXact_AppendOnly_Relation()' previously contained the code to remove the hash entry, but it was under condition of GUC 'test_AppendOnlyHash_eviction_vs_just_marking_not_inuse'. This GUC did nothing more, and was disabled by default. So, as this patch adds proper handling for the hash entry eviction, it also makes this GUC deprecated and removes its usage from 'ao-insert-eof' isolation test. --- .../access/appendonly/appendonlywriter.c | 17 +++++++++------- src/backend/utils/misc/guc_gp.c | 2 +- src/test/isolation/expected/ao-insert-eof.out | 3 +-- src/test/isolation/specs/ao-insert-eof.spec | 3 +-- src/test/regress/expected/vacuum_full_ao.out | 14 +++++++++++++ src/test/regress/sql/vacuum_full_ao.sql | 20 +++++++++++++++++++ 6 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/backend/access/appendonly/appendonlywriter.c b/src/backend/access/appendonly/appendonlywriter.c index d6124d62a7c4..bab8aff66432 100644 --- a/src/backend/access/appendonly/appendonlywriter.c +++ b/src/backend/access/appendonly/appendonlywriter.c @@ -2117,6 +2117,7 @@ AtEOXact_AppendOnly_Relation(AORelHashEntry aoentry, TransactionId currentXid) { int i; bool entry_updated = false; + bool aborted = false; /* * Only look at tables that are marked in use currently @@ -2140,6 +2141,11 @@ AtEOXact_AppendOnly_Relation(AORelHashEntry aoentry, TransactionId currentXid) } /* bingo! */ + if (segfilestat->aborted) + { + aborted = true; + } + AtEOXact_AppendOnly_StateTransition(aoentry, i, segfilestat); entry_updated = true; } @@ -2152,16 +2158,13 @@ AtEOXact_AppendOnly_Relation(AORelHashEntry aoentry, TransactionId currentXid) ereportif(Debug_appendonly_print_segfile_choice, LOG, (errmsg("AtEOXact_AppendOnly: updated txns_using_rel, it is now %d", aoentry->txns_using_rel))); - } - if (test_AppendOnlyHash_eviction_vs_just_marking_not_inuse) - { /* - * If no transaction is using this entry, it can be removed if - * hash-table gets full. So perform the same here if the above GUC is - * set. + * If the transaction was aborted, AO hash entry may contain not valid + * data left from the transaction. We remove the hash entry to allow + * the entry re-creation with the correct data on the next hash access. */ - if (!is_entry_in_use_by_other_transactions(aoentry)) + if (aborted && !is_entry_in_use_by_other_transactions(aoentry)) { AORelRemoveHashEntry(aoentry->key.relid); } diff --git a/src/backend/utils/misc/guc_gp.c b/src/backend/utils/misc/guc_gp.c index a5ad46ce88b8..7601c83b8c3c 100644 --- a/src/backend/utils/misc/guc_gp.c +++ b/src/backend/utils/misc/guc_gp.c @@ -1501,7 +1501,7 @@ struct config_bool ConfigureNamesBool_gp[] = }, { - {"test_AppendOnlyHash_eviction_vs_just_marking_not_inuse", PGC_SUSET, DEVELOPER_OPTIONS, + {"test_AppendOnlyHash_eviction_vs_just_marking_not_inuse", PGC_SUSET, DEPRECATED_OPTIONS, gettext_noop("Helps to test evicting the entry for AppendOnlyHash as soon as its usage is done instead of just marking it not inuse."), NULL, GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE diff --git a/src/test/isolation/expected/ao-insert-eof.out b/src/test/isolation/expected/ao-insert-eof.out index 52a13404b320..7aa0318fc76e 100644 --- a/src/test/isolation/expected/ao-insert-eof.out +++ b/src/test/isolation/expected/ao-insert-eof.out @@ -1,8 +1,7 @@ Parsed test spec with 2 sessions -starting permutation: s1begin s1setguc s1insert s2begin s2select s1commit s2insert s2commit s2select +starting permutation: s1begin s1insert s2begin s2select s1commit s2insert s2commit s2select step s1begin: BEGIN; -step s1setguc: SET test_AppendOnlyHash_eviction_vs_just_marking_not_inuse=1; step s1insert: INSERT INTO appendonly_eof SELECT * FROM generate_series(1, 1000); step s2begin: BEGIN ISOLATION LEVEL SERIALIZABLE; step s2select: SELECT count(*) from appendonly_eof; diff --git a/src/test/isolation/specs/ao-insert-eof.spec b/src/test/isolation/specs/ao-insert-eof.spec index 58d70e552b09..d9c3aa47f7d2 100644 --- a/src/test/isolation/specs/ao-insert-eof.spec +++ b/src/test/isolation/specs/ao-insert-eof.spec @@ -22,7 +22,6 @@ setup session "s1" step "s1begin" { BEGIN; } -step "s1setguc" { SET test_AppendOnlyHash_eviction_vs_just_marking_not_inuse=1; } step "s1insert" { INSERT INTO appendonly_eof SELECT * FROM generate_series(1, 1000); } step "s1commit" { COMMIT; } @@ -33,4 +32,4 @@ step "s2insert" { INSERT INTO appendonly_eof SELECT * FROM generate_series(1, 1 step "s2commit" { COMMIT; } -permutation "s1begin" "s1setguc" "s1insert" "s2begin" "s2select" "s1commit" "s2insert" "s2commit" "s2select" +permutation "s1begin" "s1insert" "s2begin" "s2select" "s1commit" "s2insert" "s2commit" "s2select" diff --git a/src/test/regress/expected/vacuum_full_ao.out b/src/test/regress/expected/vacuum_full_ao.out index d4d4210a42a0..7e70ddf11d83 100644 --- a/src/test/regress/expected/vacuum_full_ao.out +++ b/src/test/regress/expected/vacuum_full_ao.out @@ -92,5 +92,19 @@ vacuum table_ao_col_2; :check_segfiles_size_2 0 drop table table_ao_col_2; +-- Test vacuum for a AORO table after adding column in an aborted transaction +create table table_ao_row (i int, j int, k int) with (appendonly='true', orientation="row"); +insert into table_ao_row select i, i + 1, i + 2 from generate_series(1, 20) i; +begin; +alter table table_ao_row add column a int; +update table_ao_row set a = 1 where true; +rollback; +select cmdCheckSegmentFileSizes('table_ao_row') check_segfiles_size_aoro +\gset +delete from table_ao_row where true; +vacuum table_ao_row; +:check_segfiles_size_aoro +0 +drop table table_ao_row; drop function cmdCheckSegmentFileSizes(table_name text); drop function getTableSegFiles(t regclass, out gp_contentid smallint, out filepath text); diff --git a/src/test/regress/sql/vacuum_full_ao.sql b/src/test/regress/sql/vacuum_full_ao.sql index 73c9672b190d..205195d3b3ba 100644 --- a/src/test/regress/sql/vacuum_full_ao.sql +++ b/src/test/regress/sql/vacuum_full_ao.sql @@ -102,5 +102,25 @@ vacuum table_ao_col_2; drop table table_ao_col_2; +-- Test vacuum for a AORO table after adding column in an aborted transaction +create table table_ao_row (i int, j int, k int) with (appendonly='true', orientation="row"); +insert into table_ao_row select i, i + 1, i + 2 from generate_series(1, 20) i; + +begin; +alter table table_ao_row add column a int; +update table_ao_row set a = 1 where true; +rollback; + +select cmdCheckSegmentFileSizes('table_ao_row') check_segfiles_size_aoro +\gset + +delete from table_ao_row where true; + +vacuum table_ao_row; + +:check_segfiles_size_aoro + +drop table table_ao_row; + drop function cmdCheckSegmentFileSizes(table_name text); drop function getTableSegFiles(t regclass, out gp_contentid smallint, out filepath text); From 390d0636ff5485adea3f28b8b55aba45ca229ed3 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Tue, 16 Dec 2025 13:52:29 +0700 Subject: [PATCH 034/111] Upload built .deb packages to GitHub Releases in CI (#145) ### Update CI package job: - Target workflows `greengage-reusable-package` to `v10` tag (cache artifacts added) ### New release workflow: - Added separate `greengage-release.yml` workflow for release package upload - Triggered on release readiness (`released` event includes draft transitions) - Requires `contents: write` permissions for release operations ### New CI Action: Package Release with CI Workflow Coordination Introduces new composite action [`upload-pkgs-to-release`](https://github.com/GreengageDB/greengage-ci/tree/main/.github/actions/upload-pkgs-to-release) that coordinates between package building and release publishing workflows: - **Waits for completion of the package building workflow** (by default `Greengage CI`) before attempting release uploads - **Uses GitHub CLI to verify build success** for the specific commit and tag combination - **Restores built packages from GitHub Actions cache** using commit SHA as key - **Renames packages to standardized naming pattern**: `${PACKAGE_NAME}${VERSION}.${EXT}` - **Provides clear recovery instructions** when cache is missing, linking directly to the successful build workflow Key features: - **Configurable wait timeouts** (default 4 hours) and polling intervals (default 1 minute) for CI workflow completion - **Optional release creation** (via `create_force` flag) when release doesn't exist - **Optional asset overwrite** (via `clobber` flag) for existing release assets This action is used by the new `greengage-release.yml` workflow for automated package deployment to releases. --- .github/workflows/README.md | 35 +++++++++++++++++++++++++ .github/workflows/greengage-ci.yml | 2 +- .github/workflows/greengage-release.yml | 27 +++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/greengage-release.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 86450215b42a..1d93b3ba455e 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -37,6 +37,41 @@ operating systems: DockerHub. Runs for push to `main` (retags to `latest`) and tags (uses tag like `6.28.2`) after build. +## Release Workflow + +A separate workflow, `Greengage release`, handles the uploading of Debian packages +to GitHub releases. It is triggered when a release is published and uses a +composite action to manage package deployment. + +### Key Features + +- **Triggers:** `release: [published]` - Runs when a release is published, +including re-publishing. +- **Concurrency:** Uses the same concurrency group as the CI workflow +(`Greengage CI-${{ github.ref }}`) to ensure proper sequencing and prevent race +conditions. +- **Cache-based Artifacts:** Restores built packages from cache using the +commit SHA as the key, rather than downloading artifacts from previous jobs. +- **Manual Recovery:** If the cache is missing, the workflow checks the status +of the last build for the tag and provides clear instructions for manual +intervention. It does not automatically trigger builds to avoid infinite loops. +- **Safe Uploads:** Uploads packages with fixed naming patterns and optional +overwrite (`clobber` flag). + +### Behavior + +1. **Normal Flow (Cache Available):** Restores packages from cache, renames +them to the pattern `${PACKAGE_NAME}${VERSION}.${EXT}`, and uploads to the +release. +2. **Cache Miss Scenarios:** + - **No previous build or previous build successful:** Provides instructions + to manually trigger the CI build, then restart the release workflow. + - **Previous build failed:** Reports the failure with a link to the failed + run and requires manual fixing before retrying. + +The release workflow is designed to be robust and provide clear feedback when +issues occur, ensuring that releases are always consistent and reliable. + ## Configuration The workflow is parameterized to support flexibility: diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index 0ba7784523a1..ee96a92cd359 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -133,7 +133,7 @@ jobs: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-package.yml@v9 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-package.yml@v10 with: version: 6 target_os: ${{ matrix.target_os }} diff --git a/.github/workflows/greengage-release.yml b/.github/workflows/greengage-release.yml new file mode 100644 index 000000000000..083834935c8e --- /dev/null +++ b/.github/workflows/greengage-release.yml @@ -0,0 +1,27 @@ +# Release creation workflow +name: Greengage release + +on: + release: + types: [released] + +jobs: + upload-to-release: + strategy: + fail-fast: false + matrix: + include: + - version: 6 + extensions: deb ddeb + artifact_name: deb-packages + runs-on: ubuntu-latest + permissions: + contents: write + actions: read + steps: + - name: Upload packages to release + uses: greengagedb/greengage-ci/.github/actions/upload-pkgs-to-release@v10 + with: + version: ${{ matrix.version }} + extensions: ${{ matrix.extensions }} + artifact_name: ${{ matrix.artifact_name }} From bd39758686902a1c6a1a74b377ccc19fd340e33c Mon Sep 17 00:00:00 2001 From: Grigorii Heifetz Date: Thu, 18 Dec 2025 09:48:24 +0300 Subject: [PATCH 035/111] ADBDEV-9015: Fix gpmon const warning (#158) What happened? Usage of quote_identifier() caused a warning about discarding const qualifier when assigning return of the function to the escaped_dbname. How do we fix this? escaped_dbname should be made const char * and casted to void * inside pfree() to avoid another warning. --- src/backend/utils/gpmon/gpmon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/gpmon/gpmon.c b/src/backend/utils/gpmon/gpmon.c index 2bc0d50923d6..776f8c5756b4 100644 --- a/src/backend/utils/gpmon/gpmon.c +++ b/src/backend/utils/gpmon/gpmon.c @@ -223,7 +223,7 @@ void gpmon_qlog_packet_init(gpmon_packet_t *gpmonPacket) { const char *username = NULL; char *dbname = NULL; - char *escaped_dbname = NULL; + const char *escaped_dbname = NULL; Assert(gp_enable_gpperfmon && Gp_role == GP_ROLE_DISPATCH); Assert(gpmonPacket); @@ -249,7 +249,7 @@ void gpmon_qlog_packet_init(gpmon_packet_t *gpmonPacket) escaped_dbname ? escaped_dbname : ""); if (escaped_dbname != dbname) { - pfree(escaped_dbname); + pfree((void *) escaped_dbname); escaped_dbname = NULL; } if(dbname) From a0457d258acec64a1140b350f3f03d795c7a0aeb Mon Sep 17 00:00:00 2001 From: Vladimir Sarmin Date: Thu, 25 Dec 2025 09:13:21 +0300 Subject: [PATCH 036/111] Report resource groups to pg_stat_activity for segment backends (#154) Current state of affairs, from upstream docs: When resource groups are enabled. Only query dispatcher (QD) processes will have a rsgid and rsgname. Other server processes such as a query executer (QE) process or session connection processes will have a rsgid value of 0 and a rsgname value of unknown. QE processes are managed by the same resource group as the dispatching QD process. As a developer, I encountered a need to see backend's resource group on the segment directly via utility mode for PARALLEL RETRIEVE CURSOR. I found no adequate reason why it was done this way. 1. Only the DISPATCHER and EXECUTOR roles change resource groups. UTILITY does not interact with resource groups. 2. The patch forces segments (EXECUTOR) to report their resource group status to `pg_stat_activity`. 3. Due to point above, processes with the EXECUTOR role on the coordinator (`IS_QUERY_DISPATCHER()`) now also receive a resource group. 4. `pg_resgroup_move_query()` sends a request from the coordinator to segment processes with the same session ID, which in turn is looked up by the PID in shared memory. 5. The resource group is set only for the duration of the request and detaches at the end of the request, resetting the value in `pg_stat_activity`. The queries need to be suspended/long enough to catch their resource group in `pg_stat_activity`. 6. The resource group attaches/detaches from the process with a slight delay. This leads to the backend seeming to see its resource group only for itself between requests when querying `pg_stat_activity`. This patch makes it so resource group stats to `pg_stat_activity` on segment backends are reported, making `rsgid` and `rsgname` columns filled for the time of slice execution. Ticket: ADBDEV-8975 --- src/backend/postmaster/pgstat.c | 2 ++ src/backend/utils/resgroup/resgroup.c | 11 ++++-- .../input/resgroup/resgroup_bypass.source | 12 +++++++ .../input/resgroup/resgroup_move_query.source | 8 +++++ .../output/resgroup/resgroup_bypass.source | 24 +++++++++++++ .../resgroup/resgroup_move_query.source | 34 +++++++++++++++++++ 6 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 4052a71100a7..f6393ddf6d88 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -2956,6 +2956,8 @@ pgstat_report_resgroup(TimestampTz queueStart, Oid groupid) beentry->st_rsgid = groupid; beentry->st_changecount++; Assert((beentry->st_changecount & 1) == 0); + + elog(DEBUG1, "Reported resource group ID of %d", groupid); } /* diff --git a/src/backend/utils/resgroup/resgroup.c b/src/backend/utils/resgroup/resgroup.c index 11210c214cd2..b3b735a89d9b 100644 --- a/src/backend/utils/resgroup/resgroup.c +++ b/src/backend/utils/resgroup/resgroup.c @@ -2840,6 +2840,9 @@ SwitchResGroupOnSegment(const char *buf, int len) /* Record the bypass memory limit of current query */ self->bypassMemoryLimit = self->memUsage + RESGROUP_BYPASS_MODE_MEMORY_LIMIT_ON_QE; + + pgstat_report_resgroup(0, bypassedSlot.groupId); + return; } @@ -2913,6 +2916,8 @@ SwitchResGroupOnSegment(const char *buf, int len) /* Add into cgroup */ ResGroupOps_AssignGroup(self->groupId, &(self->caps), MyProcPid); + + pgstat_report_resgroup(0, group->groupId); } /* @@ -4735,7 +4740,7 @@ HandleMoveResourceGroup(void) ResGroupSlotData *slot; ResGroupData *group; ResGroupData *oldGroup; - Oid groupId; + Oid groupId = InvalidOid; pid_t callerPid; Assert(Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_EXECUTE); @@ -4827,8 +4832,6 @@ HandleMoveResourceGroup(void) * transaction. */ ResGroupOps_AssignGroup(self->groupId, &(self->caps), MyProcPid); - - pgstat_report_resgroup(0, self->groupId); } /* @@ -4930,6 +4933,8 @@ HandleMoveResourceGroup(void) /* Add into cgroup */ ResGroupOps_AssignGroup(self->groupId, &(self->caps), MyProcPid); } + + pgstat_report_resgroup(GetCurrentTimestamp(), groupId); } static bool diff --git a/src/test/isolation2/input/resgroup/resgroup_bypass.source b/src/test/isolation2/input/resgroup/resgroup_bypass.source index 10647d018d5a..c19130dc951f 100644 --- a/src/test/isolation2/input/resgroup/resgroup_bypass.source +++ b/src/test/isolation2/input/resgroup/resgroup_bypass.source @@ -207,6 +207,18 @@ SELECT pg_cancel_backend(pid) FROM pg_stat_activity 61q: 62q: +-- +-- resource group should be shown in pg_stat_activity even when it's bypassed +-- + +61: SET gp_resource_group_bypass TO on; +61&: SELECT pg_sleep(3) FROM gp_dist_random('gp_id'); +0U: SELECT query, rsgname FROM pg_stat_activity WHERE query LIKE '%gp_dist_random%'; +61<: + +61q: +0Uq: + -- -- cleanup -- diff --git a/src/test/isolation2/input/resgroup/resgroup_move_query.source b/src/test/isolation2/input/resgroup/resgroup_move_query.source index d65925e552bc..3fc70a555cf2 100644 --- a/src/test/isolation2/input/resgroup/resgroup_move_query.source +++ b/src/test/isolation2/input/resgroup/resgroup_move_query.source @@ -306,12 +306,20 @@ SELECT num_running FROM gp_toolkit.gp_resgroup_status WHERE rsgname='rg_move_que --spawn all backends at first short call to guarantee correct pg_resgroup_move_query() execution 1: SELECT * FROM gp_dist_random('gp_id'), pg_sleep(1) LIMIT 1; 1&: SELECT * FROM gp_dist_random('gp_id'), pg_sleep(3) LIMIT 1; +-- segments should display the resource group name +1U: SELECT rsgname, query FROM pg_stat_activity WHERE rsgname LIKE 'rg_move_query%'; +-- there are two backends on coordinator, the second one is for gather motion 2: SELECT gp_toolkit.pg_resgroup_move_query(pid, 'rg_move_query') FROM pg_stat_activity WHERE query LIKE '%pg_sleep%' AND rsgname='rg_move_query_mem_small'; +SELECT pg_sleep(1); +1U: SELECT rsgname, query FROM pg_stat_activity WHERE rsgname LIKE 'rg_move_query%'; 1<: 2: SELECT is_session_in_group(pid, 'rg_move_query') FROM pg_stat_activity WHERE query LIKE '%pg_sleep%' AND state = 'idle in transaction'; -- and check we can move it back right in the same transaction 1&: SELECT * FROM gp_dist_random('gp_id'), pg_sleep(3) LIMIT 1; +1U: SELECT rsgname, query FROM pg_stat_activity WHERE rsgname LIKE 'rg_move_query%'; 2: SELECT gp_toolkit.pg_resgroup_move_query(pid, 'rg_move_query_mem_small') FROM pg_stat_activity WHERE query LIKE '%pg_sleep%' AND rsgname='rg_move_query'; +SELECT pg_sleep(1); +1U: SELECT rsgname, query FROM pg_stat_activity WHERE rsgname LIKE 'rg_move_query%'; 1<: 2: SELECT is_session_in_group(pid, 'rg_move_query_mem_small') FROM pg_stat_activity WHERE query LIKE '%pg_sleep%' AND state = 'idle in transaction'; 1: END; diff --git a/src/test/isolation2/output/resgroup/resgroup_bypass.source b/src/test/isolation2/output/resgroup/resgroup_bypass.source index 793f100ef57e..7e5a828f6d82 100644 --- a/src/test/isolation2/output/resgroup/resgroup_bypass.source +++ b/src/test/isolation2/output/resgroup/resgroup_bypass.source @@ -357,6 +357,30 @@ ERROR: canceling statement due to user request 61q: ... 62q: ... +-- +-- resource group should be shown in pg_stat_activity even when it's bypassed +-- + +61: SET gp_resource_group_bypass TO on; +SET +61&: SELECT pg_sleep(3) FROM gp_dist_random('gp_id'); +0U: SELECT query, rsgname FROM pg_stat_activity WHERE query LIKE '%gp_dist_random%'; + query | rsgname +----------------------------------------------------------------------------------+------------- + SELECT pg_sleep(3) FROM gp_dist_random('gp_id'); | admin_group + SELECT query, rsgname FROM pg_stat_activity WHERE query LIKE '%gp_dist_random%'; | unknown +(2 rows) +61<: <... completed> + pg_sleep +---------- + + + +(3 rows) + +61q: ... +0Uq: ... + -- -- cleanup -- diff --git a/src/test/isolation2/output/resgroup/resgroup_move_query.source b/src/test/isolation2/output/resgroup/resgroup_move_query.source index 02587e1575b8..6fe47112e956 100644 --- a/src/test/isolation2/output/resgroup/resgroup_move_query.source +++ b/src/test/isolation2/output/resgroup/resgroup_move_query.source @@ -646,10 +646,28 @@ BEGIN Greenplum | -1 | -1 | -1 | (1 row) 1&: SELECT * FROM gp_dist_random('gp_id'), pg_sleep(3) LIMIT 1; +-- segments should display the resource group name +1U: SELECT rsgname, query FROM pg_stat_activity WHERE rsgname LIKE 'rg_move_query%'; + rsgname | query +-------------------------+------------------------------------------------------------- + rg_move_query_mem_small | SELECT * FROM gp_dist_random('gp_id'), pg_sleep(3) LIMIT 1; +(1 row) +-- there are two backends on coordinator, the second one is for gather motion 2: SELECT gp_toolkit.pg_resgroup_move_query(pid, 'rg_move_query') FROM pg_stat_activity WHERE query LIKE '%pg_sleep%' AND rsgname='rg_move_query_mem_small'; pg_resgroup_move_query ------------------------ t + t +(2 rows) +SELECT pg_sleep(1); + pg_sleep +---------- + +(1 row) +1U: SELECT rsgname, query FROM pg_stat_activity WHERE rsgname LIKE 'rg_move_query%'; + rsgname | query +---------------+------------------------------------------------------------- + rg_move_query | SELECT * FROM gp_dist_random('gp_id'), pg_sleep(3) LIMIT 1; (1 row) 1<: <... completed> gpname | numsegments | dbid | content | pg_sleep @@ -663,10 +681,26 @@ BEGIN (1 row) -- and check we can move it back right in the same transaction 1&: SELECT * FROM gp_dist_random('gp_id'), pg_sleep(3) LIMIT 1; +1U: SELECT rsgname, query FROM pg_stat_activity WHERE rsgname LIKE 'rg_move_query%'; + rsgname | query +---------------+------------------------------------------------------------- + rg_move_query | SELECT * FROM gp_dist_random('gp_id'), pg_sleep(3) LIMIT 1; +(1 row) 2: SELECT gp_toolkit.pg_resgroup_move_query(pid, 'rg_move_query_mem_small') FROM pg_stat_activity WHERE query LIKE '%pg_sleep%' AND rsgname='rg_move_query'; pg_resgroup_move_query ------------------------ t + t +(2 rows) +SELECT pg_sleep(1); + pg_sleep +---------- + +(1 row) +1U: SELECT rsgname, query FROM pg_stat_activity WHERE rsgname LIKE 'rg_move_query%'; + rsgname | query +-------------------------+------------------------------------------------------------- + rg_move_query_mem_small | SELECT * FROM gp_dist_random('gp_id'), pg_sleep(3) LIMIT 1; (1 row) 1<: <... completed> gpname | numsegments | dbid | content | pg_sleep From 5df8e9046f59d5571494ba9455e93c28a4479a23 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Fri, 26 Dec 2025 18:19:15 +0700 Subject: [PATCH 037/111] Port resource group tests for GG6 to KVM (#159) Switching CI to use KVM virtual machines instead of LIMA Target reusable resgroup tests to v11 tag: - copying the entire ci/ directory from Greengage image instead of single script - the logs*/ glob pattern correctly handles nested log directories Update ci/scripts/run_resgroup_test.bash: - use common logs/ directory for 7X compatible - use logs/.exitcode file for notify KVM instead of system exit code Task: ADBDEV-9031 --- .github/workflows/greengage-ci.yml | 2 +- ci/scripts/run_resgroup_test.bash | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index ee96a92cd359..ab56779a885b 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -95,7 +95,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-resgroup.yml@v1 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-resgroup.yml@v11 with: version: 6 target_os: ${{ matrix.target_os }} diff --git a/ci/scripts/run_resgroup_test.bash b/ci/scripts/run_resgroup_test.bash index d7c96e8361ae..629652f5cc24 100755 --- a/ci/scripts/run_resgroup_test.bash +++ b/ci/scripts/run_resgroup_test.bash @@ -3,11 +3,16 @@ set -eox pipefail project="resgroup" +# Exit status file for cloud-init environments where exit codes aren't propagated. +# Parent processes can read this file to determine script success/failure. +logdir="$PWD/logs" +logfile=".exitcode" + function cleanup { docker compose -p $project -f ci/docker-compose.yaml --env-file ci/.env down } -mkdir ssh_keys -p +mkdir ssh_keys "$logdir" -p if [ ! -e "ssh_keys/id_rsa" ] then ssh-keygen -P "" -f ssh_keys/id_rsa @@ -71,7 +76,11 @@ EOF1 ) EOF +# Cloud-init monitors will check for this file's existence and content. +# Missing file or invalid content will be interpreted as script failure. exitcode=$? +echo "$exitcode" > "$logdir/$logfile" + docker compose -p $project -f ci/docker-compose.yaml exec -T cdw bash -ex < Date: Fri, 26 Dec 2025 19:20:12 +0300 Subject: [PATCH 038/111] Add resource group support for parallel retrieve cursor sessions (#128) Previously, retrieve sessions ran in utility mode without resource group enforcement, leading to uncontrolled resource usage. Per SRS requirements, this patch adds new functionality. Retrieve sessions must: * get resource group limits from local user * share the same slot as their owning backend * support resource group moves * handle resource group bypass So, the patch introduces changes needed to support this feature: For parallel retrieve sessions, we now initialize session state (`MySessionState`), as it's stored in shared memory and can be used to exchange information between utility-mode retrieve connections and a backend that is running DECLARE PARALLEL RETRIEVE CURSOR on segment hosts. VMTracker is initalized too. Retrieve sessions share a slot with DECLARE backends, grabbing it from the session state. If resource group bypass is active, a new member stores the bypassed group, as in normal mode it is supposed to be dispatched by coordinator, who is unavailable in RETRIEVE sessions. When moving resource groups (via `pg_switch_resgroup()`), the session which owns the cursor now also moves RETRIEVE backends with the same session on segment hosts. Ticket: ADBDEV-8816 --- .abi-check/6.29.2/postgres.symbols.ignore | 1 + gpMgmt/bin/gpconfig | 2 +- src/backend/cdb/endpoint/cdbendpoint.c | 31 ++ .../cdb/endpoint/cdbendpointretrieve.c | 30 ++ src/backend/storage/ipc/procarray.c | 7 +- src/backend/tcop/postgres.c | 5 + src/backend/utils/init/postinit.c | 13 + src/backend/utils/misc/guc_gp.c | 18 +- src/backend/utils/resgroup/resgroup.c | 122 ++++++- .../utils/resource_manager/resource_manager.c | 15 +- src/backend/utils/session_state.c | 3 + src/include/cdb/cdbendpoint.h | 8 + src/include/utils/resgroup.h | 3 + src/include/utils/resource_manager.h | 4 + src/include/utils/session_state.h | 6 + src/include/utils/unsync_guc_name.h | 1 + .../resgroup_parallel_retrieve.source | 194 +++++++++++ .../isolation2/isolation2_resgroup_schedule | 3 + .../resgroup_parallel_retrieve.source | 326 ++++++++++++++++++ 19 files changed, 763 insertions(+), 29 deletions(-) create mode 100644 .abi-check/6.29.2/postgres.symbols.ignore create mode 100644 src/test/isolation2/input/resgroup/resgroup_parallel_retrieve.source create mode 100644 src/test/isolation2/output/resgroup/resgroup_parallel_retrieve.source diff --git a/.abi-check/6.29.2/postgres.symbols.ignore b/.abi-check/6.29.2/postgres.symbols.ignore new file mode 100644 index 000000000000..aa4c33e2be25 --- /dev/null +++ b/.abi-check/6.29.2/postgres.symbols.ignore @@ -0,0 +1 @@ +ConfigureNamesBool_gp diff --git a/gpMgmt/bin/gpconfig b/gpMgmt/bin/gpconfig index a34fe0414d7e..bc4709aa3ca3 100755 --- a/gpMgmt/bin/gpconfig +++ b/gpMgmt/bin/gpconfig @@ -39,7 +39,7 @@ except ImportError as err: EXECNAME = os.path.split(__file__)[-1] PROHIBITED_GUCS = set(["port", "listen_addresses"]) -SAMEVALUE_GUCS = set(["gp_default_storage_options"]) +SAMEVALUE_GUCS = set(["gp_default_storage_options", "gp_resource_group_retrieve"]) read_only_gucs = set() # populated at runtime LOGGER = get_default_logger() setup_tool_logging(EXECNAME, getLocalHostname(), getUserName()) diff --git a/src/backend/cdb/endpoint/cdbendpoint.c b/src/backend/cdb/endpoint/cdbendpoint.c index d59e6b8f4044..087978f9f46e 100644 --- a/src/backend/cdb/endpoint/cdbendpoint.c +++ b/src/backend/cdb/endpoint/cdbendpoint.c @@ -915,6 +915,37 @@ Endpoint return res; } +/* + * sharedEndpointsContain - Check if there's an endpoint with given receiver pid + * and session id. + */ +bool +sharedEndpointsContain(int receiverPid, int sessionId) +{ + bool res = false; + + Assert(receiverPid != InvalidPid); + Assert(sessionId != InvalidEndpointSessionId); + + LWLockAcquire(ParallelCursorEndpointLock, LW_SHARED); + + for (int i = 0; i < MAX_ENDPOINT_SIZE; ++i) + { + if (!sharedEndpoints[i].empty && + sharedEndpoints[i].sessionID == sessionId && + sharedEndpoints[i].receiverPid == receiverPid && + sharedEndpoints[i].databaseID == MyDatabaseId) + { + res = true; + break; + } + } + + LWLockRelease(ParallelCursorEndpointLock); + + return res; +} + /* * Find the token from the hash table based on given session id and user. */ diff --git a/src/backend/cdb/endpoint/cdbendpointretrieve.c b/src/backend/cdb/endpoint/cdbendpointretrieve.c index 5a0ea62d01ec..a358cd665a0b 100644 --- a/src/backend/cdb/endpoint/cdbendpointretrieve.c +++ b/src/backend/cdb/endpoint/cdbendpointretrieve.c @@ -32,6 +32,7 @@ #include "postgres.h" #include "access/xact.h" +#include "nodes/parsenodes.h" #include "storage/ipc.h" #include "utils/backend_cancel.h" #include "utils/dynahash.h" @@ -156,6 +157,12 @@ AuthEndpoint(Oid userID, const char *tokenStr) return found; } +int +RetrieveSessionId(void) +{ + return RetrieveCtl.sessionID; +} + /* * GetRetrieveStmtTupleDesc - Gets TupleDesc for the given retrieve statement. * @@ -403,6 +410,12 @@ validate_retrieve_endpoint(Endpoint *endpoint, const char *endpointName) endpointName, endpoint->receiverPid), errdetail("An endpoint can only be attached by one retrieving session."))); } + + if (ShouldUseRetrieveResGroup()) + { + /* We should have assigned the resource group already. */ + Assert(ResGroupIsAssigned() || ResGroupIsBypassed()); + } } /* @@ -749,6 +762,15 @@ retrieve_exit_callback(int code, Datum arg) detach_receiver_mq(entry); } entryHTB = NULL; + + /* + * Unassign resource group once at process exit. + * Called here after all endpoint cleanup is done. + */ + if (ShouldUnassignResGroup()) + { + UnassignResGroup(); + } } /* @@ -772,7 +794,15 @@ retrieve_xact_callback(XactEvent ev, void *arg pg_attribute_unused()) retrieve_cancel_action(RetrieveCtl.current_entry, "Endpoint retrieve statement aborted"); finish_retrieve(true); + } + /* + * Unassign resource group once at transaction abort. + * Called here after all endpoint cleanup is done. + */ + if (ShouldUnassignResGroup()) + { + UnassignResGroup(); } } } diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 5fe261bdfe9e..957e146f9d56 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -52,6 +52,7 @@ #include "access/xact.h" #include "access/twophase.h" #include "catalog/catalog.h" +#include "cdb/cdbendpoint.h" #include "miscadmin.h" #include "port/atomics.h" #include "storage/proc.h" @@ -4951,8 +4952,12 @@ ResGroupMoveSignalTarget(int sessionId, void *slot, Oid groupId, { PGPROC *proc = &allProcs[arrayP->pgprocnos[i]]; - if (proc->mppSessionId != sessionId) + /* Retrieve sessions are in utility mode. Grab them too. */ + if (proc->mppSessionId != sessionId && + !sharedEndpointsContain(proc->pid, sessionId)) + { continue; + } /* * Before, we didn't distinguish entrydb processes from main target diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 437059ff3d72..8f11a794678e 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -5400,6 +5400,11 @@ PostgresMain(int argc, char *argv[], elogif(Debug_print_full_dtm, LOG, "Simple query stmt: %s.",query_string); + if (ShouldUseRetrieveResGroup()) + { + SwitchResGroupOnRetrieveSession(); + } + if (am_walsender) exec_replication_command(query_string); else if (am_ftshandler) diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index dc9610e818a0..06fc48ab9e22 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -1154,6 +1154,19 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("retrieve connection was not authenticated for unknown reason"))); InitRetrieveCtl(); + + if (IsResGroupEnabled()) + { + /* + * Initialize now-empty SessionState and apply resource group + * limits. + */ + + gp_session_id = RetrieveSessionId(); + + SessionState_Init(); + GPMemoryProtect_Init(); + } } /* diff --git a/src/backend/utils/misc/guc_gp.c b/src/backend/utils/misc/guc_gp.c index 7601c83b8c3c..cd3e218a7a27 100644 --- a/src/backend/utils/misc/guc_gp.c +++ b/src/backend/utils/misc/guc_gp.c @@ -233,6 +233,7 @@ bool gp_resource_group_cpu_ceiling_enforcement; double gp_resource_group_memory_limit; bool gp_resource_group_bypass; bool gp_resource_group_enable_recalculate_query_mem; +bool gp_resource_group_retrieve; /* Perfmon segment GUCs */ int gp_perfmon_segment_interval; @@ -3447,6 +3448,17 @@ struct config_bool ConfigureNamesBool_gp[] = NULL, NULL, NULL }, + { + {"gp_resource_group_retrieve", PGC_SIGHUP, RESOURCES, + gettext_noop("Activate resource groups for parallel retrieve cursor sessions."), + gettext_noop("When enabled, retrieve sessions use the same resource group as the " + "session that declared the parallel retrieve cursor, sharing slots and " + "enforcing resource limits.") + }, + &gp_resource_group_retrieve, + false, NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL @@ -5559,7 +5571,11 @@ assign_pljava_classpath_insecure(bool newval, void *extra) static bool check_gp_resource_group_bypass(bool *newval, void **extra, GucSource source) { - if (!ResGroupIsAssigned()) + /* + * RETRIEVE cursor sessions exist only during a transaction. That means we + * can't really change the GUC while handler is alive. + */ + if (!ResGroupIsAssigned() && !am_cursor_retrieve_handler) return true; GUC_check_errmsg("SET gp_resource_group_bypass cannot run inside a transaction block"); diff --git a/src/backend/utils/resgroup/resgroup.c b/src/backend/utils/resgroup/resgroup.c index b3b735a89d9b..c70b60b140a9 100644 --- a/src/backend/utils/resgroup/resgroup.c +++ b/src/backend/utils/resgroup/resgroup.c @@ -46,6 +46,7 @@ #include "cdb/cdbvars.h" #include "cdb/cdbdispatchresult.h" #include "cdb/cdbdisp_query.h" +#include "cdb/cdbendpoint.h" #include "cdb/memquota.h" #include "commands/resgroupcmds.h" #include "funcapi.h" @@ -977,6 +978,12 @@ ResGroupIsAssigned(void) return selfIsAssigned(); } +bool +ResGroupIsBypassed(void) +{ + return bypassedGroup != NULL; +} + /* * Get resource group id of my proc. * @@ -2626,10 +2633,8 @@ ShouldAssignResGroupOnMaster(void) bool ShouldUnassignResGroup(void) { - return IsResGroupActivated() && - IsNormalProcessingMode() && - (Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_EXECUTE) && - !AmIInSIGUSR1Handler(); + return IsResGroupActivated() && IsNormalProcessingMode() && + IsResGroupRoleAllowed() && !AmIInSIGUSR1Handler(); } /* @@ -2680,6 +2685,9 @@ AssignResGroupOnMaster(void) bypassedSlot.memQuota = 0; bypassedSlot.memUsage = 0; + /* Share bypassed group id for RETRIEVE connections. */ + MySessionState->bypassResGroupId = groupInfo.groupId; + /* Attach self memory usage to resgroup */ groupIncMemUsage(bypassedGroup, &bypassedSlot, self->memUsage); @@ -2753,6 +2761,12 @@ UnassignResGroup(void) bypassedSlot.groupId = InvalidOid; bypassedGroup = NULL; + /* Clear shared bypass group ID. */ + if (!am_cursor_retrieve_handler) + { + MySessionState->bypassResGroupId = InvalidOid; + } + /* Update pg_stat_activity statistics */ pgstat_report_resgroup(0, InvalidOid); return; @@ -2796,21 +2810,12 @@ UnassignResGroup(void) pgstat_report_resgroup(0, InvalidOid); } -/* - * QEs are not assigned/unassigned to a resource group on segments for each - * transaction, instead, they switch resource group when a new resource group - * id or slot id is dispatched. - */ -void -SwitchResGroupOnSegment(const char *buf, int len) +static void +SwitchResGroupImpl(ResGroupCaps caps, Oid newGroupId) { - Oid newGroupId; - ResGroupCaps caps; ResGroupData *group; ResGroupSlotData *slot; - DeserializeResGroupInfo(&caps, &newGroupId, buf, len); - /* * QD will dispatch the resgroup id via bypassedSlot.groupId * in bypass mode. @@ -2831,6 +2836,11 @@ SwitchResGroupOnSegment(const char *buf, int len) Assert(bypassedGroup != NULL); + if (!am_cursor_retrieve_handler) + { + MySessionState->bypassResGroupId = bypassedSlot.groupId; + } + /* Initialize the fake slot */ bypassedSlot.memQuota = 0; bypassedSlot.memUsage = 0; @@ -2882,7 +2892,7 @@ SwitchResGroupOnSegment(const char *buf, int len) Assert(group != NULL); /* Init self */ - Assert(host_segments > 0); + Assert(host_segments > 0 || am_cursor_retrieve_handler); Assert(caps.concurrency > 0); self->caps = caps; @@ -2920,6 +2930,80 @@ SwitchResGroupOnSegment(const char *buf, int len) pgstat_report_resgroup(0, group->groupId); } +/* + * QEs are not assigned/unassigned to a resource group on segments for each + * transaction, instead, they switch resource group when a new resource group + * id or slot id is dispatched. + */ +void +SwitchResGroupOnSegment(const char *buf, int len) +{ + Oid newGroupId; + ResGroupCaps caps; + + DeserializeResGroupInfo(&caps, &newGroupId, buf, len); + + SwitchResGroupImpl(caps, newGroupId); +} + +/* + * Special path to activate resource group caps for RETRIEVE sessions. + */ +void +SwitchResGroupOnRetrieveSession(void) +{ + /* Both of these are not used when a group is to be bypassed. */ + ResGroupCaps caps = {0}; + Oid groupId = InvalidOid; + + const ResGroupSlotData *slot = MySessionState->resGroupSlot; + + Assert(am_cursor_retrieve_handler); + + if (MySessionState->bypassResGroupId != InvalidOid) + { + bypassedSlot.groupId = MySessionState->bypassResGroupId; + + SIMPLE_FAULT_INJECTOR("switch_resgroup_ppc_bypass"); + + LWLockAcquire(ResGroupLock, LW_EXCLUSIVE); + bypassedSlot.group = groupHashFind(bypassedSlot.groupId, true); + LWLockRelease(ResGroupLock); + } + else if (slot == NULL) + { + if (bypassedGroup != NULL) + { + /* Already in bypass mode. */ + Assert(bypassedGroup->groupId == bypassedSlot.groupId); + } + else + { + /* Cursor was closed while in bypass mode. */ + } + + return; + } + + if (slot != NULL) + { +#ifdef USE_ASSERT_CHECKING + /* + * We reach here even if cursor was closed on coordinator, to fail + * later. + */ + slotValidate(slot); +#endif + + SIMPLE_FAULT_INJECTOR("switch_resgroup_ppc"); + + caps = slot->caps; + groupId = slot->groupId; + } + + SwitchResGroupImpl(caps, groupId); +} + /* * Wait on the queue of resource group */ @@ -4743,7 +4827,7 @@ HandleMoveResourceGroup(void) Oid groupId = InvalidOid; pid_t callerPid; - Assert(Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_EXECUTE); + Assert(IsResGroupRoleAllowed()); /* transaction has finished */ if (!IsTransactionState() || !selfIsAssigned()) @@ -4876,8 +4960,10 @@ HandleMoveResourceGroup(void) /* * Move segment's executor. Use simple manual counters manipulation. We * can't call same complex designed for coordinator functions like above. + * Move retrieve connection if am_cursor_retrieve_handler */ - else if (Gp_role == GP_ROLE_EXECUTE && !IS_QUERY_DISPATCHER()) + else if ((Gp_role == GP_ROLE_EXECUTE || am_cursor_retrieve_handler) && + !IS_QUERY_DISPATCHER()) { SpinLockAcquire(&MyProc->movetoMutex); groupId = MyProc->movetoGroupId; diff --git a/src/backend/utils/resource_manager/resource_manager.c b/src/backend/utils/resource_manager/resource_manager.c index d3d3ff2c790b..3180094da7ad 100644 --- a/src/backend/utils/resource_manager/resource_manager.c +++ b/src/backend/utils/resource_manager/resource_manager.c @@ -15,6 +15,7 @@ #include "postgres.h" #include "cdb/cdbvars.h" +#include "cdb/cdbendpoint.h" #include "cdb/memquota.h" #include "executor/spi.h" #include "postmaster/fts.h" @@ -63,11 +64,9 @@ InitResManager(void) InitResQueues(); } - else if (IsResGroupEnabled() && - (Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_EXECUTE) && - IsUnderPostmaster && - !amAuxiliaryBgWorker() && - !am_walsender && !am_ftshandler && !am_faulthandler) + else if (IsResGroupEnabled() && IsResGroupRoleAllowed() && + IsUnderPostmaster && !amAuxiliaryBgWorker() && !am_walsender && + !am_ftshandler && !am_faulthandler) { /* * InitResManager() is called under PostgresMain(), so resource group is not @@ -98,8 +97,8 @@ InitResManager(void) SPI_InitMemoryReservation(); } - if (MySessionState && - !IsBackgroundWorker && - (Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_EXECUTE)) + if (MySessionState && !IsBackgroundWorker && IsResGroupRoleAllowed()) + { GPMemoryProtect_TrackStartupMemory(); + } } diff --git a/src/backend/utils/session_state.c b/src/backend/utils/session_state.c index 6e6e6bfb468d..25cedf13051f 100644 --- a/src/backend/utils/session_state.c +++ b/src/backend/utils/session_state.c @@ -19,6 +19,7 @@ #include "utils/memutils.h" #include "cdb/cdbvars.h" +#include "cdb/cdbendpoint.h" #include "miscadmin.h" #include "port/atomics.h" #include "storage/lwlock.h" @@ -101,6 +102,7 @@ SessionState_Acquire(int sessionId) acquired->idle_start = 0; acquired->latestCursorCommandId = 0; acquired->resGroupSlot = NULL; + acquired->bypassResGroupId = InvalidOid; #ifdef USE_ASSERT_CHECKING acquired->isModifiedSessionId = false; @@ -176,6 +178,7 @@ SessionState_Release(SessionState *acquired) acquired->latestCursorCommandId = 0; Assert(acquired->resGroupSlot == NULL); acquired->resGroupSlot = NULL; + acquired->bypassResGroupId = InvalidOid; #ifdef USE_ASSERT_CHECKING acquired->isModifiedSessionId = false; diff --git a/src/include/cdb/cdbendpoint.h b/src/include/cdb/cdbendpoint.h index 155e185a7ea1..339d3ccd7837 100644 --- a/src/include/cdb/cdbendpoint.h +++ b/src/include/cdb/cdbendpoint.h @@ -30,6 +30,8 @@ #include "executor/tqueue.h" #include "storage/shm_toc.h" #include "nodes/execnodes.h" +#include "utils/resgroup.h" +#include "utils/resource_manager.h" /* * Endpoint allocate positions. @@ -57,6 +59,10 @@ enum EndPointExecPosition #define ENDPOINT_READY_ACK_MSG "ENDPOINT_READY" #define ENDPOINT_FINISHED_ACK_MSG "ENDPOINT_FINISHED" +#define ShouldUseRetrieveResGroup() \ + (am_cursor_retrieve_handler && gp_resource_group_retrieve && \ + IsResGroupActivated()) + /* * Endpoint attach status, used by parallel retrieve cursor. */ @@ -160,10 +166,12 @@ extern bool AuthEndpoint(Oid userID, const char *tokenStr); extern TupleDesc GetRetrieveStmtTupleDesc(const RetrieveStmt *stmt); extern void ExecRetrieveStmt(const RetrieveStmt *stmt, DestReceiver *dest); extern void check_parallel_retrieve_cursor_errors(EState *estate); +extern int RetrieveSessionId(void); /* Endpoint shared memory utility functions in "cdbendpoint.c" */ extern Endpoint *get_endpointdesc_by_index(int index); extern void get_token_from_session_hashtable(int sessionId, Oid userID, int8 *token /* out */ ); +extern bool sharedEndpointsContain(int receiverPid, int sessionId); /* utility functions in "cdbendpointutilities.c" */ extern void endpoint_token_str2arr(const char *tokenStr, int8 *token); diff --git a/src/include/utils/resgroup.h b/src/include/utils/resgroup.h index c20bc8674cc0..e40a3ab74266 100644 --- a/src/include/utils/resgroup.h +++ b/src/include/utils/resgroup.h @@ -107,6 +107,7 @@ extern bool gp_resource_group_bypass; extern int gp_resource_group_queuing_timeout; extern bool gp_resource_group_bypass_catalog_query; extern int gp_resource_group_move_timeout; +extern bool gp_resource_group_retrieve; /* * Non-GUC global variables. @@ -171,8 +172,10 @@ extern bool ShouldUnassignResGroup(void); extern void AssignResGroupOnMaster(void); extern void UnassignResGroup(void); extern void SwitchResGroupOnSegment(const char *buf, int len); +extern void SwitchResGroupOnRetrieveSession(void); extern bool ResGroupIsAssigned(void); +extern bool ResGroupIsBypassed(void); /* Retrieve statistic information of type from resource group */ extern Datum ResGroupGetStat(Oid groupId, ResGroupStatType type); diff --git a/src/include/utils/resource_manager.h b/src/include/utils/resource_manager.h index 007c93f23673..be0d939f272e 100644 --- a/src/include/utils/resource_manager.h +++ b/src/include/utils/resource_manager.h @@ -37,6 +37,10 @@ #define IsResGroupActivated() \ (ResGroupActivated) +#define IsResGroupRoleAllowed() \ + (Gp_role == GP_ROLE_DISPATCH || Gp_role == GP_ROLE_EXECUTE || \ + am_cursor_retrieve_handler) + typedef enum { RESOURCE_MANAGER_POLICY_QUEUE, diff --git a/src/include/utils/session_state.h b/src/include/utils/session_state.h index 230c19c8e28f..3c2e325720a3 100644 --- a/src/include/utils/session_state.h +++ b/src/include/utils/session_state.h @@ -99,6 +99,12 @@ typedef struct SessionState /* MyProc->queryCommandId of the latest cursor command in this session */ int latestCursorCommandId; + /* + * When gp_resource_group_bypass is enabled, stores the bypass group ID + * so retrieve sessions can grab it from the backend holding the cursor. + */ + Oid bypassResGroupId; + #ifdef USE_ASSERT_CHECKING /* If we modify the sessionId in ProcMppSessionId, this field is turned on */ bool isModifiedSessionId; diff --git a/src/include/utils/unsync_guc_name.h b/src/include/utils/unsync_guc_name.h index c996235f8f5a..8f5151eb3af1 100644 --- a/src/include/utils/unsync_guc_name.h +++ b/src/include/utils/unsync_guc_name.h @@ -247,6 +247,7 @@ "gp_resource_group_queuing_timeout", "gp_resource_group_enable_recalculate_query_mem", "gp_resource_group_move_timeout", + "gp_resource_group_retrieve", "gp_resource_manager", "gp_resqueue_memory_policy", "gp_resqueue_priority", diff --git a/src/test/isolation2/input/resgroup/resgroup_parallel_retrieve.source b/src/test/isolation2/input/resgroup/resgroup_parallel_retrieve.source new file mode 100644 index 000000000000..d9c597c6c1c4 --- /dev/null +++ b/src/test/isolation2/input/resgroup/resgroup_parallel_retrieve.source @@ -0,0 +1,194 @@ +-- start_ignore +CREATE EXTENSION IF NOT EXISTS gp_parallel_retrieve_cursor; + +DROP TABLE IF EXISTS t CASCADE; +DROP ROLE r; +DROP RESOURCE GROUP rg; +DROP RESOURCE GROUP rg2; + +CREATE TABLE t (i INT) DISTRIBUTED BY (i); +CREATE RESOURCE GROUP rg WITH (concurrency=10, cpu_rate_limit=10, memory_limit=20); +CREATE RESOURCE GROUP rg2 WITH (concurrency=10, cpu_rate_limit=10, memory_limit=40); + +CREATE ROLE r SUPERUSER RESOURCE GROUP rg2; + +INSERT INTO t SELECT generate_series(1, 7); + +GRANT ALL ON t TO r; + +ALTER ROLE gpadmin WITH RESOURCE GROUP rg; +-- end_ignore +-- start_matchsubs +-- m/ERROR: (.*) \([a-zA-Z_]+\.c:[0-9]+\)$/ +-- s/ERROR: (.*) \([a-zA-Z_]+\.c:[0-9]+\)$/ERROR: $1/ +-- end_matchsubs + +-- +-- Normal mode with GUC enabled. +-- + +!\retcode gpconfig -c gp_resource_group_retrieve -v on; +!\retcode gpstop -u; + +1: BEGIN; +1: DECLARE c1 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(2), i FROM t; +1: @post_run 'parse_endpoint_info 1 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c1'; + +1: SELECT gp_inject_fault('switch_resgroup_ppc', 'skip', dbid) + FROM gp_segment_configuration WHERE role = 'p' and content = 0; +1&: SELECT gp_wait_until_triggered_fault('switch_resgroup_ppc', 1, dbid) + FROM gp_segment_configuration WHERE role = 'p' and content = 0; + +0R&: @pre_run 'set_endpoint_variable @ENDPOINT1': RETRIEVE 1 FROM ENDPOINT "@ENDPOINT1"; + +0U: SHOW gp_resource_group_retrieve; +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE 1%'; + +0R<: +1<: + +1: SELECT gp_inject_fault('all', 'reset', dbid) + FROM gp_segment_configuration WHERE role = 'p' and content = 0; +1q: + +-- Retrieve on closed cursor. +0R: @pre_run 'set_endpoint_variable @ENDPOINT1': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT1"; +0Rq: + +-- +-- Normal mode with GUC disabled. +-- + +!\retcode gpconfig -c gp_resource_group_retrieve -v off; +!\retcode gpstop -u; + +1: BEGIN; +1: DECLARE c2 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(2), i FROM t; +1: @post_run 'parse_endpoint_info 2 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c2'; + +0R&: @pre_run 'set_endpoint_variable @ENDPOINT2': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT2"; + +0U: SHOW gp_resource_group_retrieve; +-- No resource groups assigned. +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; + +0R<: +0Rq: +1: ABORT; + +!\retcode gpconfig -c gp_resource_group_retrieve -v on; +!\retcode gpconfig -c gp_resource_group_bypass -v on; +!\retcode gpstop -u; + +-- +-- Bypass mode with GUC enabled. +-- + +1: BEGIN; + +1: DECLARE c3 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(2), i FROM t; +1: @post_run 'parse_endpoint_info 3 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c3'; + +0R&: @pre_run 'set_endpoint_variable @ENDPOINT3': RETRIEVE 1 FROM ENDPOINT "@ENDPOINT3"; + +0U: SHOW gp_resource_group_bypass; +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE 1%'; + +0R<: + +-- All subsequent queries are run with bypass mode. + +1: SELECT gp_inject_fault('switch_resgroup_ppc_bypass', 'skip', dbid), + gp_inject_fault('switch_resgroup_ppc', 'error', dbid) + FROM gp_segment_configuration WHERE role = 'p' and content = 0; +1&: SELECT gp_wait_until_triggered_fault('switch_resgroup_ppc_bypass', 1, dbid) + FROM gp_segment_configuration WHERE role = 'p' and content = 0; + +0R: @pre_run 'set_endpoint_variable @ENDPOINT3': RETRIEVE 1 FROM ENDPOINT "@ENDPOINT3"; + +1<: + +1: SELECT gp_inject_fault('all', 'reset', dbid) + FROM gp_segment_configuration WHERE role = 'p' and content = 0; +1q: + +-- Retrieve on closed cursor with bypass mode. +0R: @pre_run 'set_endpoint_variable @ENDPOINT3': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT3"; +0Rq: + +!\retcode gpconfig -r gp_resource_group_bypass; +!\retcode gpstop -u; + +-- +-- Multiple retrieve sessions sharing slot. +-- + +1: BEGIN; + +1: DECLARE c4 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(3), i FROM t; +1: @post_run 'parse_endpoint_info 4 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c4'; +1: DECLARE c5 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(3), i FROM t; +1: @post_run 'parse_endpoint_info 5 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c5'; +1: DECLARE c6 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(3), i FROM t; +1: @post_run 'parse_endpoint_info 6 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c6'; + +0R&: @pre_run 'set_endpoint_variable @ENDPOINT4': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT4"; +1R&: @pre_run 'set_endpoint_variable @ENDPOINT5': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT5"; +2R&: @pre_run 'set_endpoint_variable @ENDPOINT6': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT6"; + +-- All should have the same resource group. +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; +1U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; +2U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; + +0R<: +1R<: +2R<: + +0Rq: + +1: COMMIT; +1q: + +-- +-- GUC value must be the same on segments and on the master. +-- + +!\retcode gpconfig -c gp_resource_group_retrieve -v on -m off; + +--- +--- Moving resource group should move the retrieve sessions as well. +--- + +1: BEGIN; + +1: DECLARE c7 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(5), i FROM t; +1: @post_run 'parse_endpoint_info 7 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c7'; + +0R&: @pre_run 'set_endpoint_variable @ENDPOINT7': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT7"; + +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; + +2: SET ROLE r; +2: SELECT gp_toolkit.pg_resgroup_move_query(pid, 'rg2') FROM pg_stat_activity WHERE rsgname = 'rg'; + +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; + +0R<: + +1: ABORT; + +1q: +0Rq: +0Uq: +-- start_ignore +! gpconfig -r gp_resource_group_retrieve; +! gpstop -u; + +ALTER ROLE gpadmin WITH RESOURCE GROUP admin_group; + +DROP TABLE t; +DROP ROLE r; +DROP RESOURCE GROUP rg; +DROP RESOURCE GROUP rg2; +-- end_ignore diff --git a/src/test/isolation2/isolation2_resgroup_schedule b/src/test/isolation2/isolation2_resgroup_schedule index 142323ff5467..9116f39cb46d 100644 --- a/src/test/isolation2/isolation2_resgroup_schedule +++ b/src/test/isolation2/isolation2_resgroup_schedule @@ -63,4 +63,7 @@ test: resgroup/resgroup_startup_memory test: resgroup/resgroup_oom +# parallel retrieve cursor integration +test: resgroup/resgroup_parallel_retrieve + test: resgroup/disable_resgroup diff --git a/src/test/isolation2/output/resgroup/resgroup_parallel_retrieve.source b/src/test/isolation2/output/resgroup/resgroup_parallel_retrieve.source new file mode 100644 index 000000000000..9dcb375e68a5 --- /dev/null +++ b/src/test/isolation2/output/resgroup/resgroup_parallel_retrieve.source @@ -0,0 +1,326 @@ +-- start_matchsubs +-- m/ERROR: (.*) \([a-zA-Z_]+\.c:[0-9]+\)$/ +-- s/ERROR: (.*) \([a-zA-Z_]+\.c:[0-9]+\)$/ERROR: $1/ +-- end_matchsubs + +-- +-- Normal mode with GUC enabled. +-- + +!\retcode gpconfig -c gp_resource_group_retrieve -v on; +(exited with code 0) +!\retcode gpstop -u; +(exited with code 0) + +1: BEGIN; +BEGIN +1: DECLARE c1 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(2), i FROM t; +DECLARE +1: @post_run 'parse_endpoint_info 1 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c1'; + endpoint_id1 | token_id | host_id | port_id | READY + endpoint_id1 | token_id | host_id | port_id | READY + endpoint_id1 | token_id | host_id | port_id | READY +(3 rows) + +1: SELECT gp_inject_fault('switch_resgroup_ppc', 'skip', dbid) FROM gp_segment_configuration WHERE role = 'p' and content = 0; + gp_inject_fault +----------------- + Success: +(1 row) +1&: SELECT gp_wait_until_triggered_fault('switch_resgroup_ppc', 1, dbid) FROM gp_segment_configuration WHERE role = 'p' and content = 0; + +0R&: @pre_run 'set_endpoint_variable @ENDPOINT1': RETRIEVE 1 FROM ENDPOINT "@ENDPOINT1"; + +0U: SHOW gp_resource_group_retrieve; + gp_resource_group_retrieve +---------------------------- + on +(1 row) +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE 1%'; + rsgname | query +---------+------------------------------------------------ + rg | RETRIEVE 1 FROM ENDPOINT "endpoint_id1 "; +(1 row) + +0R<: <... completed> + pg_sleep | i +----------+--- + | 2 +(1 row) +1<: <... completed> + gp_wait_until_triggered_fault +------------------------------- + Success: +(1 row) + +1: SELECT gp_inject_fault('all', 'reset', dbid) FROM gp_segment_configuration WHERE role = 'p' and content = 0; + gp_inject_fault +----------------- + Success: +(1 row) +1q: ... + +-- Retrieve on closed cursor. +0R: @pre_run 'set_endpoint_variable @ENDPOINT1': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT1"; +ERROR: endpoint is not available because the parallel retrieve cursor was aborted +0Rq: ... + +-- +-- Normal mode with GUC disabled. +-- + +!\retcode gpconfig -c gp_resource_group_retrieve -v off; +(exited with code 0) +!\retcode gpstop -u; +(exited with code 0) + +1: BEGIN; +BEGIN +1: DECLARE c2 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(2), i FROM t; +DECLARE +1: @post_run 'parse_endpoint_info 2 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c2'; + endpoint_id2 | token_id | host_id | port_id | READY + endpoint_id2 | token_id | host_id | port_id | READY + endpoint_id2 | token_id | host_id | port_id | READY +(3 rows) + +0R&: @pre_run 'set_endpoint_variable @ENDPOINT2': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT2"; + +0U: SHOW gp_resource_group_retrieve; + gp_resource_group_retrieve +---------------------------- + off +(1 row) +-- No resource groups assigned. +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; + rsgname | query +---------+-------------------------------------------------- + unknown | RETRIEVE ALL FROM ENDPOINT "endpoint_id2 "; +(1 row) + +0R<: <... completed> + pg_sleep | i +----------+--- + | 2 + | 3 + | 4 + | 7 +(4 rows) +0Rq: ... +1: ABORT; +ABORT + +!\retcode gpconfig -c gp_resource_group_retrieve -v on; +(exited with code 0) +!\retcode gpconfig -c gp_resource_group_bypass -v on; +(exited with code 0) +!\retcode gpstop -u; +(exited with code 0) + +-- +-- Bypass mode with GUC enabled. +-- + +1: BEGIN; +BEGIN + +1: DECLARE c3 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(2), i FROM t; +DECLARE +1: @post_run 'parse_endpoint_info 3 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c3'; + endpoint_id3 | token_id | host_id | port_id | READY + endpoint_id3 | token_id | host_id | port_id | READY + endpoint_id3 | token_id | host_id | port_id | READY +(3 rows) + +0R&: @pre_run 'set_endpoint_variable @ENDPOINT3': RETRIEVE 1 FROM ENDPOINT "@ENDPOINT3"; + +0U: SHOW gp_resource_group_bypass; + gp_resource_group_bypass +-------------------------- + on +(1 row) +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE 1%'; + rsgname | query +---------+------------------------------------------------ + rg | RETRIEVE 1 FROM ENDPOINT "endpoint_id3 "; +(1 row) + +0R<: <... completed> + pg_sleep | i +----------+--- + | 2 +(1 row) + +-- All subsequent queries are run with bypass mode. + +1: SELECT gp_inject_fault('switch_resgroup_ppc_bypass', 'skip', dbid), gp_inject_fault('switch_resgroup_ppc', 'error', dbid) FROM gp_segment_configuration WHERE role = 'p' and content = 0; + gp_inject_fault | gp_inject_fault +-----------------+----------------- + Success: | Success: +(1 row) +1&: SELECT gp_wait_until_triggered_fault('switch_resgroup_ppc_bypass', 1, dbid) FROM gp_segment_configuration WHERE role = 'p' and content = 0; + +0R: @pre_run 'set_endpoint_variable @ENDPOINT3': RETRIEVE 1 FROM ENDPOINT "@ENDPOINT3"; + pg_sleep | i +----------+--- + | 3 +(1 row) + +1<: <... completed> + gp_wait_until_triggered_fault +------------------------------- + Success: +(1 row) + +1: SELECT gp_inject_fault('all', 'reset', dbid) FROM gp_segment_configuration WHERE role = 'p' and content = 0; + gp_inject_fault +----------------- + Success: +(1 row) +1q: ... + +-- Retrieve on closed cursor with bypass mode. +0R: @pre_run 'set_endpoint_variable @ENDPOINT3': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT3"; +ERROR: endpoint is not available because the parallel retrieve cursor was aborted +0Rq: ... + +!\retcode gpconfig -r gp_resource_group_bypass; +(exited with code 0) +!\retcode gpstop -u; +(exited with code 0) + +-- +-- Multiple retrieve sessions sharing slot. +-- + +1: BEGIN; +BEGIN + +1: DECLARE c4 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(3), i FROM t; +DECLARE +1: @post_run 'parse_endpoint_info 4 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c4'; + endpoint_id4 | token_id | host_id | port_id | READY + endpoint_id4 | token_id | host_id | port_id | READY + endpoint_id4 | token_id | host_id | port_id | READY +(3 rows) +1: DECLARE c5 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(3), i FROM t; +DECLARE +1: @post_run 'parse_endpoint_info 5 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c5'; + endpoint_id5 | token_id | host_id | port_id | READY + endpoint_id5 | token_id | host_id | port_id | READY + endpoint_id5 | token_id | host_id | port_id | READY +(3 rows) +1: DECLARE c6 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(3), i FROM t; +DECLARE +1: @post_run 'parse_endpoint_info 6 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c6'; + endpoint_id6 | token_id | host_id | port_id | READY + endpoint_id6 | token_id | host_id | port_id | READY + endpoint_id6 | token_id | host_id | port_id | READY +(3 rows) + +0R&: @pre_run 'set_endpoint_variable @ENDPOINT4': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT4"; +1R&: @pre_run 'set_endpoint_variable @ENDPOINT5': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT5"; +2R&: @pre_run 'set_endpoint_variable @ENDPOINT6': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT6"; + +-- All should have the same resource group. +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; + rsgname | query +---------+-------------------------------------------------- + rg | RETRIEVE ALL FROM ENDPOINT "endpoint_id4 "; +(1 row) +1U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; + rsgname | query +---------+-------------------------------------------------- + rg | RETRIEVE ALL FROM ENDPOINT "endpoint_id5 "; +(1 row) +2U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; + rsgname | query +---------+-------------------------------------------------- + rg | RETRIEVE ALL FROM ENDPOINT "endpoint_id6 "; +(1 row) + +0R<: <... completed> + pg_sleep | i +----------+--- + | 2 + | 3 + | 4 + | 7 +(4 rows) +1R<: <... completed> + pg_sleep | i +----------+--- + | 1 +(1 row) +2R<: <... completed> + pg_sleep | i +----------+--- + | 5 + | 6 +(2 rows) + +0Rq: ... + +1: COMMIT; +COMMIT +1q: ... + +-- +-- GUC value must be the same on segments and on the master. +-- + +!\retcode gpconfig -c gp_resource_group_retrieve -v on -m off; +(exited with code 1) + +--- +--- Moving resource group should move the retrieve sessions as well. +--- + +1: BEGIN; +BEGIN + +1: DECLARE c7 PARALLEL RETRIEVE CURSOR FOR SELECT pg_sleep(5), i FROM t; +DECLARE +1: @post_run 'parse_endpoint_info 7 1 2 3 4': SELECT endpointname, auth_token, hostname, port, state FROM gp_get_endpoints() WHERE cursorname = 'c7'; + endpoint_id7 | token_id | host_id | port_id | READY + endpoint_id7 | token_id | host_id | port_id | READY + endpoint_id7 | token_id | host_id | port_id | READY +(3 rows) + +0R&: @pre_run 'set_endpoint_variable @ENDPOINT7': RETRIEVE ALL FROM ENDPOINT "@ENDPOINT7"; + +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; + rsgname | query +---------+-------------------------------------------------- + rg | RETRIEVE ALL FROM ENDPOINT "endpoint_id7 "; +(1 row) + +2: SET ROLE r; +SET +2: SELECT gp_toolkit.pg_resgroup_move_query(pid, 'rg2') FROM pg_stat_activity WHERE rsgname = 'rg'; + pg_resgroup_move_query +------------------------ + t +(1 row) + +0U: SELECT rsgname, query FROM pg_stat_activity WHERE query LIKE 'RETRIEVE ALL%'; + rsgname | query +---------+-------------------------------------------------- + rg2 | RETRIEVE ALL FROM ENDPOINT "endpoint_id7 "; +(1 row) + +0R<: <... completed> + pg_sleep | i +----------+--- + | 2 + | 3 + | 4 + | 7 +(4 rows) + +1: ABORT; +ABORT + +1q: ... +0Rq: ... +0Uq: ... From e3bae2b669a2b819ec0391ff2d4dc15f8a6536a0 Mon Sep 17 00:00:00 2001 From: Grigorii Heifetz Date: Mon, 29 Dec 2025 08:52:50 +0300 Subject: [PATCH 039/111] ADBDEV-9025: Prohibit skip-level correlated queries in Postgres planner (#141) Prohibit skip-level correlated queries in Postgres planner What occurs? Postgres planner creates an invalid plan with skip-level correlation involving master-only and replicated tables. The plan fails to be executed during execution. Why it occurs? Skip-level correlation (at least for distributed tables) seems to be prohibited in Postgres planner due to rescanning issues that arise - they appear, because the lowest level plan is made an initplan without correct motion node applied. ORCA planner produces valid plans for such queries, but not with master-only tables - it seems to lack a feature to work with them whatsoever. How do we fix this? Let's prohibit any skip-level correlation for Postgres Legacy planner as it leads to rescan issues because of absent motions. Alternative fixes? Changing some checks for initplans creation and enabling skip-level correlation make Postgres produces correct plan - initplan is changed to subplan and correct motions are applied. Yet, one test from rangefuncs fails. So, some research is needed. Tests? Newly added tests check that ORCA is able to plan and execute simple skip-level correlated plans, whereas Postgres planner outputs an error about lack of skip-level correlation support. --- src/backend/optimizer/plan/subselect.c | 12 +- .../regress/expected/qp_correlated_query.out | 102 +++++++++ .../qp_correlated_query_optimizer.out | 206 ++++++++++++++++++ src/test/regress/sql/qp_correlated_query.sql | 104 +++++++++ 4 files changed, 415 insertions(+), 9 deletions(-) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index d6133e82ae20..a29407713b87 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -420,7 +420,8 @@ get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod, } /** - * Returns true if query refers to a distributed table. + * Returns true if query refers to any table on this level or on some other + * subquery level, if recursive is true. */ bool QueryHasDistributedRelation(Query *q, bool recursive) { @@ -435,17 +436,10 @@ bool QueryHasDistributedRelation(Query *q, bool recursive) && QueryHasDistributedRelation(rte->subquery, true)) return true; + /* Really, any kind of distribution policy causes rescan issues */ if (rte->relid != InvalidOid && rte->rtekind == RTE_RELATION) - { - GpPolicy *policy = GpPolicyFetch(rte->relid); - if (GpPolicyIsPartitioned(policy)) - { - pfree(policy); return true; - } - pfree(policy); - } } return false; } diff --git a/src/test/regress/expected/qp_correlated_query.out b/src/test/regress/expected/qp_correlated_query.out index 1b4eb1d3078e..87e335036e85 100644 --- a/src/test/regress/expected/qp_correlated_query.out +++ b/src/test/regress/expected/qp_correlated_query.out @@ -3911,6 +3911,108 @@ DROP TABLE skip_correlated_t4; reset optimizer_join_order; reset optimizer_trace_fallback; -------------------------------------------------------------------------------- +-- ORCA should be able to plan and execute correctly one skip-level queries, but +-- not with master-only tables. Postgres Legacy planner should give an error to +-- any skip-level query. +-------------------------------------------------------------------------------- +CREATE TABLE skip_correlated_partitioned ( + a INT +) DISTRIBUTED BY (a); +INSERT INTO skip_correlated_partitioned VALUES (1), (2), (3); +CREATE TABLE skip_correlated_random ( + b INT +) DISTRIBUTED RANDOMLY; +INSERT INTO skip_correlated_random VALUES (1), (2), (3); +CREATE TABLE skip_correlated_replicated ( + c INT +) DISTRIBUTED REPLICATED; +INSERT INTO skip_correlated_replicated VALUES(1), (2), (3); +-- easy cases +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) +) FROM skip_correlated_replicated; +ERROR: correlated subquery with skip-level correlations is not supported +SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) +) FROM skip_correlated_replicated ORDER BY a; +ERROR: correlated subquery with skip-level correlations is not supported +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT b FROM skip_correlated_random WHERE b = a + ) +) FROM skip_correlated_partitioned; +ERROR: correlated subquery with skip-level correlations is not supported +SELECT ( + SELECT ( + SELECT b FROM skip_correlated_random WHERE b = a + ) +) FROM skip_correlated_partitioned ORDER BY b; +ERROR: correlated subquery with skip-level correlations is not supported +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT c FROM skip_correlated_replicated WHERE c = a + ) +) FROM skip_correlated_partitioned; +ERROR: correlated subquery with skip-level correlations is not supported +SELECT ( + SELECT ( + SELECT c FROM skip_correlated_replicated WHERE c = a + ) +) FROM skip_correlated_partitioned ORDER BY c; +ERROR: correlated subquery with skip-level correlations is not supported +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT dbid FROM gp_segment_configuration WHERE dbid = a + ) +) FROM skip_correlated_partitioned; +ERROR: correlated subquery with skip-level correlations is not supported +-- hard cases +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; +ERROR: correlated subquery with skip-level correlations is not supported +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; +ERROR: correlated subquery with skip-level correlations is not supported +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; +ERROR: correlated subquery with skip-level correlations is not supported +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; +ERROR: correlated subquery with skip-level correlations is not supported +DROP TABLE skip_correlated_partitioned; +DROP TABLE skip_correlated_random; +DROP TABLE skip_correlated_replicated; +-------------------------------------------------------------------------------- -- Ensure ORCA generates the correct plan with the exists clause -- for the partitioned table. -------------------------------------------------------------------------------- diff --git a/src/test/regress/expected/qp_correlated_query_optimizer.out b/src/test/regress/expected/qp_correlated_query_optimizer.out index 3db92119c070..9cdd0ff52c6a 100644 --- a/src/test/regress/expected/qp_correlated_query_optimizer.out +++ b/src/test/regress/expected/qp_correlated_query_optimizer.out @@ -4061,6 +4061,212 @@ DROP TABLE skip_correlated_t4; reset optimizer_join_order; reset optimizer_trace_fallback; -------------------------------------------------------------------------------- +-- ORCA should be able to plan and execute correctly one skip-level queries, but +-- not with master-only tables. Postgres Legacy planner should give an error to +-- any skip-level query. +-------------------------------------------------------------------------------- +CREATE TABLE skip_correlated_partitioned ( + a INT +) DISTRIBUTED BY (a); +INSERT INTO skip_correlated_partitioned VALUES (1), (2), (3); +CREATE TABLE skip_correlated_random ( + b INT +) DISTRIBUTED RANDOMLY; +INSERT INTO skip_correlated_random VALUES (1), (2), (3); +CREATE TABLE skip_correlated_replicated ( + c INT +) DISTRIBUTED REPLICATED; +INSERT INTO skip_correlated_replicated VALUES(1), (2), (3); +-- easy cases +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) +) FROM skip_correlated_replicated; + QUERY PLAN +---------------------------------------------------------------------------------- + Result + -> Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on skip_correlated_replicated + SubPlan 1 (slice0) + -> Result + Filter: (skip_correlated_partitioned.a = skip_correlated_replicated.c) + -> Materialize + -> Gather Motion 3:1 (slice2; segments: 3) + -> Seq Scan on skip_correlated_partitioned + Optimizer: Pivotal Optimizer (GPORCA) +(10 rows) + +SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) +) FROM skip_correlated_replicated ORDER BY a; + a +--- + 1 + 2 + 3 +(3 rows) + +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT b FROM skip_correlated_random WHERE b = a + ) +) FROM skip_correlated_partitioned; + QUERY PLAN +------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice2; segments: 3) + -> Result + -> Seq Scan on skip_correlated_partitioned + SubPlan 1 (slice2; segments: 3) + -> Result + Filter: (skip_correlated_random.b = skip_correlated_partitioned.a) + -> Materialize + -> Broadcast Motion 3:3 (slice1; segments: 3) + -> Seq Scan on skip_correlated_random + Optimizer: Pivotal Optimizer (GPORCA) +(10 rows) + +SELECT ( + SELECT ( + SELECT b FROM skip_correlated_random WHERE b = a + ) +) FROM skip_correlated_partitioned ORDER BY b; + b +--- + 1 + 2 + 3 +(3 rows) + +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT c FROM skip_correlated_replicated WHERE c = a + ) +) FROM skip_correlated_partitioned; + QUERY PLAN +------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + -> Result + -> Seq Scan on skip_correlated_partitioned + SubPlan 1 (slice1; segments: 3) + -> Seq Scan on skip_correlated_replicated + Filter: (c = skip_correlated_partitioned.a) + Optimizer: Pivotal Optimizer (GPORCA) +(7 rows) + +SELECT ( + SELECT ( + SELECT c FROM skip_correlated_replicated WHERE c = a + ) +) FROM skip_correlated_partitioned ORDER BY c; + c +--- + 1 + 2 + 3 +(3 rows) + +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT dbid FROM gp_segment_configuration WHERE dbid = a + ) +) FROM skip_correlated_partitioned; +ERROR: correlated subquery with skip-level correlations is not supported +-- hard cases +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; + QUERY PLAN +---------------------------------------------------------------------------------------- + Sort + Sort Key: ((SubPlan 1)) + -> Result + -> Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on skip_correlated_replicated + SubPlan 1 (slice0) + -> Result + Filter: (skip_correlated_partitioned.a = skip_correlated_replicated.c) + -> Materialize + -> Gather Motion 3:1 (slice2; segments: 3) + -> Seq Scan on skip_correlated_partitioned + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) + +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; + l1 +---- + 1 + 2 + 3 +(3 rows) + +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: ((SubPlan 2)) + -> Result + -> Gather Motion 1:1 (slice1; segments: 1) + -> Seq Scan on skip_correlated_replicated + SubPlan 2 (slice0) + -> Limit + -> Sort + Sort Key: ((SubPlan 1)) + -> Result + -> Materialize + -> Gather Motion 3:1 (slice2; segments: 3) + -> Seq Scan on skip_correlated_random + SubPlan 1 (slice0) + -> Result + Filter: ((skip_correlated_partitioned.a = skip_correlated_replicated.c) AND (skip_correlated_partitioned.a = skip_correlated_random.b)) + -> Materialize + -> Gather Motion 3:1 (slice3; segments: 3) + -> Seq Scan on skip_correlated_partitioned + Optimizer: Pivotal Optimizer (GPORCA) +(20 rows) + +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; + l1 +---- + 1 + 2 + 3 +(3 rows) + +DROP TABLE skip_correlated_partitioned; +DROP TABLE skip_correlated_random; +DROP TABLE skip_correlated_replicated; +-------------------------------------------------------------------------------- -- Ensure ORCA generates the correct plan with the exists clause -- for the partitioned table. -------------------------------------------------------------------------------- diff --git a/src/test/regress/sql/qp_correlated_query.sql b/src/test/regress/sql/qp_correlated_query.sql index b32528153583..3aa38b1715c3 100644 --- a/src/test/regress/sql/qp_correlated_query.sql +++ b/src/test/regress/sql/qp_correlated_query.sql @@ -833,7 +833,111 @@ DROP TABLE skip_correlated_t3; DROP TABLE skip_correlated_t4; reset optimizer_join_order; reset optimizer_trace_fallback; +-------------------------------------------------------------------------------- +-- ORCA should be able to plan and execute correctly one skip-level queries, but +-- not with master-only tables. Postgres Legacy planner should give an error to +-- any skip-level query. +-------------------------------------------------------------------------------- +--start_ignore +DROP TABLE IF EXISTS skip_correlated_partitioned; +DROP TABLE IF EXISTS skip_correlated_random; +DROP TABLE IF EXISTS skip_correlated_replicated; +--end_ignore +CREATE TABLE skip_correlated_partitioned ( + a INT +) DISTRIBUTED BY (a); +INSERT INTO skip_correlated_partitioned VALUES (1), (2), (3); + +CREATE TABLE skip_correlated_random ( + b INT +) DISTRIBUTED RANDOMLY; +INSERT INTO skip_correlated_random VALUES (1), (2), (3); + +CREATE TABLE skip_correlated_replicated ( + c INT +) DISTRIBUTED REPLICATED; +INSERT INTO skip_correlated_replicated VALUES(1), (2), (3); + +-- easy cases +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) +) FROM skip_correlated_replicated; +SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) +) FROM skip_correlated_replicated ORDER BY a; + +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT b FROM skip_correlated_random WHERE b = a + ) +) FROM skip_correlated_partitioned; +SELECT ( + SELECT ( + SELECT b FROM skip_correlated_random WHERE b = a + ) +) FROM skip_correlated_partitioned ORDER BY b; + +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT c FROM skip_correlated_replicated WHERE c = a + ) +) FROM skip_correlated_partitioned; +SELECT ( + SELECT ( + SELECT c FROM skip_correlated_replicated WHERE c = a + ) +) FROM skip_correlated_partitioned ORDER BY c; + +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT dbid FROM gp_segment_configuration WHERE dbid = a + ) +) FROM skip_correlated_partitioned; + +-- hard cases +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c + ) + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; + +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; +SELECT ( + SELECT ( + SELECT ( + SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 + ) +) AS l1 FROM skip_correlated_replicated ORDER BY l1; +DROP TABLE skip_correlated_partitioned; +DROP TABLE skip_correlated_random; +DROP TABLE skip_correlated_replicated; -------------------------------------------------------------------------------- -- Ensure ORCA generates the correct plan with the exists clause -- for the partitioned table. From edaf53aa94fd64c48efea42f4a8b69227ee51c75 Mon Sep 17 00:00:00 2001 From: Ivan Sergeenko Date: Mon, 29 Dec 2025 14:12:10 +0300 Subject: [PATCH 040/111] Fix Orca CCTETest unit test (#162) The patch fixes a test added in b1b31a2 that was broken because of the cost model changes related to hash join introduced in 55a0fcb. As a result of these changes, both hash joins present in the test are expected to have a higher cost. The fix for the issue is a simple update of the plan cost reported by the test that makes it comply with the current cost model. (cherry picked from commit 5d7693ec9f9f19c5f76a4b93ca09bd6037f570c3) --- .../dxl/minidump/CTEMergeGroupsCircularDeriveStats2.mdp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats2.mdp b/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats2.mdp index 6eb9bdb457df..68c8400dd801 100644 --- a/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats2.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTEMergeGroupsCircularDeriveStats2.mdp @@ -366,7 +366,7 @@ - + @@ -392,7 +392,7 @@ - + @@ -424,7 +424,7 @@ - + @@ -446,7 +446,7 @@ - + From c00148b01f4bdde1ea0e0bb178a6d4f80d01ff46 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Mon, 12 Jan 2026 15:32:31 +0700 Subject: [PATCH 041/111] Target ORCA tests CI to v12 tag (#168) Fix ORCA unit tests to properly fail on errors - Remove unconditional redundant success fallback (|| true) from log copy command - Capture test script exit code in variable for proper propagation - Exit container with actual test status instead of last command result - Use single quotes to avoid shell variable expansion in container context Task: ADBDEV-9063 --- .github/workflows/greengage-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index ab56779a885b..5b16d0618819 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -77,7 +77,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-orca.yml@v9 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-orca.yml@v12 with: version: 6 target_os: ${{ matrix.target_os }} From 23efb07740edae19405117d57fd0b638b4b9ce19 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Fri, 16 Jan 2026 12:18:32 +0700 Subject: [PATCH 042/111] Optimize Greengage 6 development image for size (#174) Dockerfile Improvements: - Fix FromAsCasing: `as` and `FROM` keywords casing do not match - Set `SHELL` directive with `pipefail` for safer script execution - Consolidated `RUN` commands to reduce layer count - Added cleanup steps to reduce image size (`apt-get clean`, removing cache) - Remove `VOLUME`: there is no point in specifying it in the Dockerfile - `WORKDIR`moved up to eliminate the Hadolint warning at `Dockerfile.ubuntu` line 3: `COPY` to a relative destination without `WORKDIR` set - Improved `wget` output with `--progress=dot:giga`, `--no-hsts` to prevent `.wget-hsts` file creation - Combined `ENV` declarations into single statements - Used `pip install --no-cache-dir` to avoid caching pip packages Package Management: - Moved all package installations to `README.ubuntu.bash` for consistency - Add `DEBIAN_FRONTEND=noninteractive` to `README.ubuntu.bash` - Remove `openjdk-11-jdk` package as deprecated and not required Build Optimization: - Replaced `cp` with `ln` (hard links) in `compile_gpdb.bash` Task: ADBDEV-9054 --- README.ubuntu.bash | 12 +++++++-- ci/Dockerfile.ubuntu | 41 +++++++++++++---------------- concourse/scripts/compile_gpdb.bash | 2 +- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/README.ubuntu.bash b/README.ubuntu.bash index 0042dc4e04bd..0285e641ff8d 100755 --- a/README.ubuntu.bash +++ b/README.ubuntu.bash @@ -1,20 +1,24 @@ #!/bin/bash - +# Some packages, for example KRB5, not installing properly without this option +export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y \ bison \ build-essential \ cmake \ + curl \ debhelper \ devscripts \ dh-python \ - curl \ + fakeroot \ flex \ g++ \ gcc \ git \ iproute2 \ iputils-ping \ + krb5-admin-server \ + krb5-kdc \ libapr1-dev \ libaprutil1-dev \ libbz2-dev \ @@ -38,7 +42,11 @@ apt-get install -y \ openssh-client \ openssh-server \ pkg-config \ + protobuf-compiler \ + python-pip \ python2 \ python2-dev \ + python3-dev \ rsync \ + sudo \ zlib1g-dev diff --git a/ci/Dockerfile.ubuntu b/ci/Dockerfile.ubuntu index c97b643f859e..140787cf79cb 100644 --- a/ci/Dockerfile.ubuntu +++ b/ci/Dockerfile.ubuntu @@ -1,4 +1,6 @@ -FROM ubuntu:22.04 as base +FROM ubuntu:22.04 AS base +SHELL ["/bin/bash", "-o", "pipefail", "-c"] +WORKDIR /home/gpadmin COPY README.ubuntu.bash ./ RUN set -eux; \ @@ -6,7 +8,7 @@ RUN set -eux; \ rm README.ubuntu.bash; \ ln -s python2 /usr/bin/python; \ # Install pg_bsd_indent used by pgindent utility - wget https://ftp.postgresql.org/pub/dev/pg_bsd_indent-1.3.tar.gz -O - | tar -xzf -; \ + wget --progress=dot:giga --no-hsts https://ftp.postgresql.org/pub/dev/pg_bsd_indent-1.3.tar.gz -O - | tar -xzf -; \ make install -C pg_bsd_indent; \ rm -r pg_bsd_indent; \ # The en_US.UTF-8 locale is needed to run GPDB @@ -15,44 +17,39 @@ RUN set -eux; \ mkdir /run/sshd; \ # Alter precedence in favor of IPv4 during resolving echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf; \ -# Packages for tests - DEBIAN_FRONTEND=noninteractive \ - apt install -y krb5-kdc krb5-admin-server fakeroot sudo python-pip \ - openjdk-11-jdk protobuf-compiler python3-dev; \ # Install allure-behave for behave tests - pip2 install allure-behave==2.4.0; \ - pip2 cache purge; + pip2 install --no-cache-dir allure-behave==2.4.0 && \ +# Cleanup to reduce image size + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/cache/man/ -WORKDIR /home/gpadmin ENV CONFIGURE_FLAGS="--enable-debug-extensions --with-gssapi --enable-cassert --enable-debug --enable-depend" -FROM base as build +FROM base AS build COPY . gpdb_src -RUN mkdir /home/gpadmin/bin_gpdb +RUN mkdir bin_gpdb -ENV TARGET_OS=ubuntu -ENV OUTPUT_ARTIFACT_DIR=bin_gpdb +ENV TARGET_OS=ubuntu \ + OUTPUT_ARTIFACT_DIR=bin_gpdb \ # Use python3.9 to compile into GPDB's plpython3u -ENV PYTHON3=python3 + PYTHON3=python3 # Compile with running mocking tests -RUN bash /home/gpadmin/gpdb_src/concourse/scripts/compile_gpdb.bash +RUN gpdb_src/concourse/scripts/compile_gpdb.bash -FROM base as code +FROM base AS code # Use --exclude, when it will be available in stable syntax. COPY . gpdb_src RUN rm -rf gpdb_src/.git/ -FROM base as test -COPY --from=code /home/gpadmin/gpdb_src gpdb_src -COPY --from=build /home/gpadmin/bin_gpdb /home/gpadmin/bin_gpdb +FROM base AS test + +COPY --from=code /home/gpadmin/gpdb_src gpdb_src +COPY --from=build /home/gpadmin/bin_gpdb bin_gpdb COPY --from=build /home/gpadmin/gpdb_src/VERSION gpdb_src # Install entab used by pgindent utility. # This should be done using gpdb sources. RUN make -C gpdb_src/src/tools/entab install clean - -# Volume for tests output -VOLUME /home/gpadmin/gpdb_src/src/test diff --git a/concourse/scripts/compile_gpdb.bash b/concourse/scripts/compile_gpdb.bash index 22127287a6ac..9092a83855f5 100755 --- a/concourse/scripts/compile_gpdb.bash +++ b/concourse/scripts/compile_gpdb.bash @@ -146,7 +146,7 @@ function export_gpdb() { tar -czf "${TARBALL}" ./* popd - cp "${TARBALL}" "${server_build}" + ln "${TARBALL}" "${server_build}" } function export_gpdb_extensions() { From fd60597c8c332c2852ddff0cb3e95906bbd73341 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Fri, 16 Jan 2026 16:07:18 +0300 Subject: [PATCH 043/111] Actualize README files for ubuntu (#93) Information in README files regarding building Greengage on ubuntu and installing python modules yaml and psutil for gpMgmt seems to be incorrect. This commit corrects it. Changes include: - Describe the recommended way to build Greengage using the gpAux build system, as it contains preffered configure options for supported operating systems. - Remove explicit configure option sets from README files. - Recommend use of the user home directory on a development machine for development builds via using GPHOME variable. - Add --enable-debug-extensions for the development build to the gpAux Makefile, it enables fault injector required for the tests running. - Always set optimizer_enable_table_alias GUC to false for the demo cluster and eliminate the temporary shell variable OPTIMIZER_ENABLE_TABLE_ALIAS. It was set only for Orca CI tests, but this is harmless for Postgres optimizer. This allows running Orca tests with simple `make installcheck-world` command. - Install pip for gpMgmt using Ubuntu package python-pip instead of get-pip. The command in the package named pip2, so change it accordingly. --- README.linux.md | 22 ++-------- README.md | 77 ++++++++++++++++++++++------------- concourse/scripts/common.bash | 4 -- gpAux/Makefile | 2 +- gpAux/gpdemo/demo_cluster.sh | 12 +++--- gpMgmt/bin/Makefile | 4 +- gpMgmt/bin/README | 5 +-- 7 files changed, 63 insertions(+), 63 deletions(-) diff --git a/README.linux.md b/README.linux.md index fb5e754faa1c..8a114a34a725 100644 --- a/README.linux.md +++ b/README.linux.md @@ -57,7 +57,7 @@ ./README.CentOS.bash ``` -## For Ubuntu (versions 20.04 or 22.04): +## For Ubuntu (22.04): - Install dependencies using README.ubuntu.bash script: ```bash @@ -85,19 +85,7 @@ ## Common Platform Tasks: -1. Create gpadmin and setup ssh keys - - Either use: - - ```bash - # Requires gpdb clone to be named gpdb_src - gpdb_src/concourse/scripts/setup_gpadmin_user.bash - ``` - to create the gpadmin user and set up keys, - - OR - - manually create ssh keys so you can do ssh localhost without a password, e.g., +1. Setup SSH keys so you can run ssh localhost without a password, e.g., ```bash ssh-keygen @@ -108,7 +96,7 @@ 2. Verify that you can ssh to your machine name without a password ```bash - ssh # e.g., ssh briarwood + ssh `hostname` # e.g., ssh briarwood ``` 3. Set up your system configuration: @@ -150,7 +138,3 @@ su - $USER # Apply settings ``` -5. Make sure that you download yaml and psutil as submodules. To do this, use `git clone --recurse-submodules` when downloading the source code. If you want to update the submodules, run: - ```bash - git submodule update --init --recursive --force - ``` diff --git a/README.md b/README.md index 16eb0e7e2b0d..75cd868bd59b 100644 --- a/README.md +++ b/README.md @@ -37,56 +37,79 @@ Follow [these macOS steps](README.macOS.md) for getting your system ready for GP ### Installing dependencies (for Linux developers) Follow [appropriate linux steps](README.linux.md) for getting your system ready for GPDB +### Downloading submodules +Make sure that you download submodules. To do this, use `git clone --recurse-submodules` +when downloading the source code. If you want to update the submodules, run: +```bash +git submodule update --init --recursive --force +``` + ### Build the database +The recommended way to build the database is to use build system located in the gpAux +directory, which is also used for CI testing and building packages. + +To create optimized release build, use the following: + +``` +make GPROOT=~/build PARALLEL_MAKE_OPTS=-j8 dist -C gpAux +``` + +To run regression tests, a debug build with debug extensions is required. It can be built using the following command: + +``` +make GPROOT=~/build PARALLEL_MAKE_OPTS=-j8 devel -C gpAux ``` -# Configure build environment to install at /usr/local/gpdb -./configure --with-perl --with-python --with-libxml --with-gssapi --prefix=/usr/local/gpdb -# Compile and install -make -j8 -make -j8 install +Load the greengage environment into your current shell: -# Bring in greengage environment into your running shell -source /usr/local/gpdb/greengage_path.sh +``` +source ~/build/greengage-db-devel/greengage_path.sh +``` + +Start demo cluster: -# Start demo cluster +``` make create-demo-cluster -# (gpdemo-env.sh contains __PGPORT__ and __MASTER_DATA_DIRECTORY__ values) +``` + +To use the demo cluster, source the environment variables from gpdemo-env.sh, which contains +__PGPORT__ and __MASTER_DATA_DIRECTORY__ values: + +``` source gpAux/gpdemo/gpdemo-env.sh ``` The directory, the TCP ports, the number of segments, and the existence of standbys for segments and coordinator for the demo cluster can be changed -on the fly. +when starting the demo cluster. Instead of `make create-demo-cluster`, consider: ``` DATADIRS=/tmp/gpdb-cluster PORT_BASE=5555 NUM_PRIMARY_MIRROR_PAIRS=1 WITH_MIRRORS=false make create-demo-cluster ``` -The TCP port for the regression test can be changed on the fly: - +If you want to clean all generated files: ``` -PGPORT=5555 make installcheck-world +make distclean ``` -To turn GPORCA off and use Postgres planner for query optimization: +## Running tests + +* By default, tests use the GPORCA optimizer: ``` -set optimizer=off; +make installcheck-world ``` -If you want to clean all generated files +* To turn GPORCA off and use the Postgres planner for query optimization: ``` -make distclean +PGOPTIONS='-c optimizer=off' make installcheck-world ``` -## Running tests - -* The default regression tests +* The TCP port for the regression test can be changed: ``` -make installcheck-world +PGPORT=5555 make installcheck-world ``` * The top-level target __installcheck-world__ will run all regression @@ -112,17 +135,15 @@ make installcheck-world ## Alternative Configurations +Internally, the configure script is used to adapt the build system to the characteristics of the host machine. For finer control over the components that are built, configuration options must be specified explicitly. The configuration options in effect are recorded in the file config.log and may be inspected with the following command: +``` +head config.log +``` + ### Building GPDB without GPORCA Currently, GPDB is built with GPORCA by default. If you want to build GPDB without GPORCA, configure requires `--disable-orca` flag to be set. -``` -# Clean environment -make distclean - -# Configure build environment to install at /usr/local/gpdb -./configure --disable-orca --with-perl --with-python --with-libxml --prefix=/usr/local/gpdb -``` ### Building GPDB with gpperfmon enabled diff --git a/concourse/scripts/common.bash b/concourse/scripts/common.bash index 2b9a52e2b3c5..873a4d130a53 100644 --- a/concourse/scripts/common.bash +++ b/concourse/scripts/common.bash @@ -88,10 +88,6 @@ function make_cluster() { export BLDWRAP_POSTGRES_CONF_ADDONS=${BLDWRAP_POSTGRES_CONF_ADDONS} export STATEMENT_MEM=250MB - if [[ "$MAKE_TEST_COMMAND" =~ "optimizer=on" ]]; then - export OPTIMIZER_ENABLE_TABLE_ALIAS=off - fi - pushd gpdb_src/gpAux/gpdemo su gpadmin -c "source /usr/local/greengage-db-devel/greengage_path.sh; make create-demo-cluster WITH_MIRRORS=${WITH_MIRRORS:-true}" diff --git a/gpAux/Makefile b/gpAux/Makefile index 2513a5ae2eb1..a7330c897669 100644 --- a/gpAux/Makefile +++ b/gpAux/Makefile @@ -292,7 +292,7 @@ endif devel : INSTCFLAGS=$(DEBUGFLAGS) devel : INSTLOC=$(DESTDIR)$(DEVPATH) devel : CLIENTSINSTLOC=$(DESTDIR)$(CLIENTSDEVPATH) -devel : CONFIGFLAGS+= --enable-cassert --enable-debug --enable-depend +devel : CONFIGFLAGS+= --enable-cassert --enable-debug --enable-debug-extensions --enable-depend ifdef ENABLE_VPATH_BUILD devel : BUILDDIR=Debug devel : ISCONFIG=$(BUILDDIR)/GNUmakefile diff --git a/gpAux/gpdemo/demo_cluster.sh b/gpAux/gpdemo/demo_cluster.sh index 26715322123f..68740c1408fb 100755 --- a/gpAux/gpdemo/demo_cluster.sh +++ b/gpAux/gpdemo/demo_cluster.sh @@ -392,11 +392,13 @@ if [ -n "${STATEMENT_MEM}" ]; then EOF fi -if [ -n "${OPTIMIZER_ENABLE_TABLE_ALIAS}" ]; then - cat >> $CLUSTER_CONFIG_POSTGRES_ADDONS<<-EOF - optimizer_enable_table_alias = ${OPTIMIZER_ENABLE_TABLE_ALIAS} - EOF -fi +# Demo cluster always works with disabled optimizer_enable_table_alias, so it's +# easy to run tests suite with no additional setup. +# TODO: remove this and GUC optimizer_enable_table_alias when tests are +# corrected for aliases. +cat >> $CLUSTER_CONFIG_POSTGRES_ADDONS<<-EOF +optimizer_enable_table_alias = false +EOF if [ "${ONLY_PREPARE_CLUSTER_ENV}" == "true" ]; then echo "ONLY_PREPARE_CLUSTER_ENV set, generated clusterConf file: $CLUSTER_CONFIG, exiting" diff --git a/gpMgmt/bin/Makefile b/gpMgmt/bin/Makefile index 565941916ddc..f47387faee15 100644 --- a/gpMgmt/bin/Makefile +++ b/gpMgmt/bin/Makefile @@ -175,9 +175,7 @@ pylint: $(MOCK_BIN): @echo "--- mock for platform $(UBUNTU_PLATFORM)" @if [ $(UBUNTU_PLATFORM) = "Ubuntu" ]; then\ - wget https://bootstrap.pypa.io/pip/2.7/get-pip.py && \ - python2 get-pip.py && \ - pip install mock==1.0.1;\ + pip2 install mock==1.0.1;\ else\ mkdir -p $(PYTHONSRC_INSTALL_SITE) && \ cd $(PYLIB_SRC_EXT)/ && tar xzf $(MOCK_DIR).tar.gz && \ diff --git a/gpMgmt/bin/README b/gpMgmt/bin/README index ea0ff98334b1..7815e283c76c 100644 --- a/gpMgmt/bin/README +++ b/gpMgmt/bin/README @@ -155,10 +155,9 @@ Testing Management Scripts This directory contains the unit tests for the management scripts. These tests require the following Python modules to be installed: mock and pygresql. -These modules can be installed by running "git submodule update --init --recursive" -if they are not already installed on your machine. +These modules can be installed by running "make install" in gpMgmt/bin. -If you installed the dependencies using the above git command, you can run the tests with +If you installed the dependencies using the command above, you can run the tests with make, using the following commands in the current directory: "make check" will run all of the unit tests, some of which require a GPDB cluster to From 199c94998d9b9919d7b1163596cffdddc1c9b1bc Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Wed, 21 Jan 2026 18:22:15 +0700 Subject: [PATCH 044/111] Remove caching standard Ubuntu image for 6.x (#178) Retarget CI to v13 tag: - greengage-reusable-tests-resgroup.ym - disable try to cache/restore Ubuntu cloud image - greengage-reusable-build.yml - optimize cache image (don't try to restore, save only) Task: ADBDEV-9050 --- .github/workflows/greengage-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index 5b16d0618819..dd9ac9c700e7 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -23,7 +23,7 @@ jobs: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-build.yml@v8 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-build.yml@v13 with: version: 6 target_os: ${{ matrix.target_os }} @@ -95,7 +95,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-resgroup.yml@v11 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-resgroup.yml@v13 with: version: 6 target_os: ${{ matrix.target_os }} From 36995b08f24a162192743a262f7b0218bab5b232 Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Thu, 22 Jan 2026 11:42:23 +0300 Subject: [PATCH 045/111] Actualize tests for gpMgmt (#172) There are tests under gpMgmt/bin/test which cover gpMgmt python code. It seems that tests were not in use for some time and became broken. This commit restores tests for python 2.7 on both Ubuntu 22.04 and Centos. Changes include: - Up test version in gpcheckcat test to the supported one (6.2) - Explicitly set mocked value for opened stream encoding to string utf-8 as old python 2.7.5 from CentOS 7 uses it as an argument for string.decode() - Use wider template for log files, so they now include compiled python base name - Use pip in Makefile to install mock unconditionally, eliminating difference between platforms To run tests one can use make check in gpMgmt directory. Ticket: GG-79 --- gpMgmt/bin/Makefile | 15 +++------------ .../bin/gppylib/test/unit/test_unit_gpcheckcat.py | 2 +- .../bin/gppylib/test/unit/test_unit_gpexpand.py | 6 +++++- .../gppylib/test/unit/test_unit_gpsegrecovery.py | 4 ++-- .../test/unit/test_unit_gpsegsetuprecovery.py | 4 ++-- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/gpMgmt/bin/Makefile b/gpMgmt/bin/Makefile index f47387faee15..006aa9056ef1 100644 --- a/gpMgmt/bin/Makefile +++ b/gpMgmt/bin/Makefile @@ -155,8 +155,6 @@ PYTHONSRC_INSTALL=$(PYLIB_SRC_EXT)/install PYTHON_VERSION=$(shell python -c "import sys; print ('%s.%s' % (sys.version_info[0:2]))") PYTHONSRC_INSTALL_SITE=$(PYLIB_SRC_EXT)/install/lib/python$(PYTHON_VERSION)/site-packages PYTHONSRC_INSTALL_PYTHON_PATH=$(PYTHONPATH):$(PYTHONSRC_INSTALL_SITE) -# TODO: mock-1.0.1-py2.6.egg package should be updated. -MOCK_BIN=$(PYTHONSRC_INSTALL)/lib/python$(PYTHON_VERSION)/site-packages/mock-1.0.1-py2.6.egg UBUNTU_PLATFORM=$(shell if lsb_release -a 2>/dev/null | grep -q 'Ubuntu' ; then echo "Ubuntu"; fi) pylint: @@ -172,16 +170,9 @@ pylint: @touch $(PYLIB_SRC_EXT)/$(PYLINT_DIR)/build/lib/__init__.py @touch $(PYLIB_SRC_EXT)/$(PYLINT_DIR)/build/lib/logilab/__init__.py -$(MOCK_BIN): +mockinstall: @echo "--- mock for platform $(UBUNTU_PLATFORM)" - @if [ $(UBUNTU_PLATFORM) = "Ubuntu" ]; then\ - pip2 install mock==1.0.1;\ - else\ - mkdir -p $(PYTHONSRC_INSTALL_SITE) && \ - cd $(PYLIB_SRC_EXT)/ && tar xzf $(MOCK_DIR).tar.gz && \ - cd $(PYLIB_SRC_EXT)/$(MOCK_DIR)/ && \ - PYTHONPATH=$(PYTHONSRC_INSTALL_PYTHON_PATH) python setup.py install --prefix $(PYTHONSRC_INSTALL) ; \ - fi; + python -m pip install $(PYLIB_SRC_EXT)/$(MOCK_DIR).tar.gz; \ PYTHON_FILES=`grep -l --exclude=Makefile --exclude=gplogfilter "/bin/env python" *`\ `grep -l "/bin/env python" $(SRC)/../sbin/*`\ @@ -199,7 +190,7 @@ check-regress: @PYTHONPATH=$(SRC):$(SRC)/ext:$(PYTHONPATH) \ gppylib/gpunit discover --verbose -s gppylib -p "test_regress*.py" 2> $(SRC)/../gpMgmt_testregress_results.log 1> $(SRC)/../gpMgmt_testregress_output.log -check: $(MOCK_BIN) +check: mockinstall @echo "Running pure unit and also "unit" tests that require cluster to be up..." @TMPDIR=/tmp PYTHONPATH=$(SERVER_SRC):$(SERVER_SBIN):$(PYTHONPATH):$(PYTHONSRC_INSTALL_PYTHON_PATH):$(SRC)/ext:$(SBIN_DIR):$(LIB_DIR):$(PYLIB_DIR)/mock-1.0.1 \ gppylib/gpunit discover --verbose -s $(SRC)/gppylib -p "test_unit*.py" 2> $(SRC)/../gpMgmt_testunit_results.log 1> $(SRC)/../gpMgmt_testunit_output.log diff --git a/gpMgmt/bin/gppylib/test/unit/test_unit_gpcheckcat.py b/gpMgmt/bin/gppylib/test/unit/test_unit_gpcheckcat.py index 3850bd5fa6ec..eaae6336aa25 100755 --- a/gpMgmt/bin/gppylib/test/unit/test_unit_gpcheckcat.py +++ b/gpMgmt/bin/gppylib/test/unit/test_unit_gpcheckcat.py @@ -145,7 +145,7 @@ def test_truncate_batch_size(self, mock_log, mock_gpcheckcat, mock_sys_exit): for i in range(1, 50): primaries.append(dict(hostname='host0', port=123, id=1, address='123', datadir='dir', content=1, dbid=i, isprimary='t')) - self.db_connection.query.return_value.getresult.return_value = [['4.3']] + self.db_connection.query.return_value.getresult.return_value = [['6.2']] self.db_connection.query.return_value.dictresult.return_value = primaries testargs = ['some_string','-port 1', '-R foo'] diff --git a/gpMgmt/bin/gppylib/test/unit/test_unit_gpexpand.py b/gpMgmt/bin/gppylib/test/unit/test_unit_gpexpand.py index 0196ebf8862b..b36d56ed9b66 100644 --- a/gpMgmt/bin/gppylib/test/unit/test_unit_gpexpand.py +++ b/gpMgmt/bin/gppylib/test/unit/test_unit_gpexpand.py @@ -32,9 +32,13 @@ def setUp(self): self.subject.is_gpexpand_running = Mock(return_value=False) self.gparray = self.createGpArrayWith2Primary2Mirrors() + + open_mock = mock_open() + open_mock.return_value.encoding = 'utf-8' + self.apply_patches([ patch('gpexpand.GpArray.initFromCatalog', return_value=self.gparray), - patch('__builtin__.open', mock_open(), create=True), + patch('__builtin__.open', open_mock, create=True), patch('__builtin__.raw_input'), patch('gpexpand.copy.deepcopy', return_value=Mock()), patch('gpexpand.dbconn.execSQL', return_value=FakeCursor()), diff --git a/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegrecovery.py b/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegrecovery.py index 56ba305cfa94..e3c1a8a62ae1 100644 --- a/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegrecovery.py +++ b/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegrecovery.py @@ -310,7 +310,7 @@ def test_complete_workflow(self, mock_pgbasebackup_run, mock_pgbasebackup_init, self.assertEqual(1, mock_pgrewind_init.call_count) self.assertEqual(1, mock_pgbasebackup_run.call_count) self.assertEqual(1, mock_pgbasebackup_init.call_count) - self.assertRegexpMatches(gplog.get_logfile(), '/gpsegrecovery.py_\d+\.log') + self.assertRegexpMatches(gplog.get_logfile(), '/gpsegrecovery.pyc?_\d+\.log') @patch('gppylib.commands.pg.PgRewind.__init__', return_value=None) @patch('gppylib.commands.pg.PgRewind.run') @@ -338,7 +338,7 @@ def test_complete_workflow_exception(self, mock_pgbasebackup_run, mock_pgbasebac self.assertEqual(1, mock_pgrewind_init.call_count) self.assertEqual(1, mock_pgbasebackup_run.call_count) self.assertEqual(1, mock_pgbasebackup_init.call_count) - self.assertRegexpMatches(gplog.get_logfile(), '/gpsegrecovery.py_\d+\.log') + self.assertRegexpMatches(gplog.get_logfile(), '/gpsegrecovery.pyc?_\d+\.log') @patch('recovery_base.gplog.setup_tool_logging') @patch('recovery_base.RecoveryBase.main') diff --git a/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegsetuprecovery.py b/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegsetuprecovery.py index 72639d4b69d9..c0021cee1702 100644 --- a/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegsetuprecovery.py +++ b/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegsetuprecovery.py @@ -334,7 +334,7 @@ def test_complete_workflow(self, mock_execsql, mock_dburl, mock_connect, mock_va mock_connect.assert_called_once() mock_execsql.assert_called_once() #TODO use regex pattern - self.assertRegexpMatches(gplog.get_logfile(), '/gpsegsetuprecovery.py_\d+\.log') + self.assertRegexpMatches(gplog.get_logfile(), '/gpsegsetuprecovery.pyc?_\d+\.log') @patch('gpsegsetuprecovery.ValidationForFullRecovery.validate_failover_data_directory') @patch('gpsegsetuprecovery.dbconn.connect') @@ -357,7 +357,7 @@ def test_complete_workflow_exception(self, mock_execsql, mock_dburl, mock_connec mock_validate_datadir.assert_called_once() mock_dburl.assert_called_once() mock_connect.assert_called_once() - self.assertRegexpMatches(gplog.get_logfile(), '/gpsegsetuprecovery.py_\d+\.log') + self.assertRegexpMatches(gplog.get_logfile(), '/gpsegsetuprecovery.pyc?_\d+\.log') @patch('recovery_base.gplog.setup_tool_logging') From e8d337690215801ee4edf8ff3b35412613cea6db Mon Sep 17 00:00:00 2001 From: Georgy Shelkovy Date: Thu, 22 Jan 2026 14:12:49 +0500 Subject: [PATCH 046/111] Fix the gprecoverseg keeps segment logs test (#191) Commit d306009 added a new "gprecoverseg keeps segment logs" test to the gpMgmt/test/behave/mgmt_utils/gprecoverseg.feature file, which left the cluster in an inconsistent state. Subsequent concourse_cluster tests would hang. Restore the cluster at the end of the test. Co-authored-by: Vasiliy Ivanov Ticket: GG-115 --- gpMgmt/test/behave/mgmt_utils/gprecoverseg.feature | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gpMgmt/test/behave/mgmt_utils/gprecoverseg.feature b/gpMgmt/test/behave/mgmt_utils/gprecoverseg.feature index 466e5209ccae..9b6379ae26c2 100644 --- a/gpMgmt/test/behave/mgmt_utils/gprecoverseg.feature +++ b/gpMgmt/test/behave/mgmt_utils/gprecoverseg.feature @@ -736,6 +736,8 @@ Feature: gprecoverseg tests And user can start transactions And the user runs "gprecoverseg -a" Then gprecoverseg should return a return code of 0 + And the segments are synchronized + And the cluster is rebalanced And the "primary" segment pg_log directory content preserved From 5ae66badd647197c5831f62725d03752870adb49 Mon Sep 17 00:00:00 2001 From: Vladislav Pavlov Date: Thu, 22 Jan 2026 15:48:58 +0300 Subject: [PATCH 047/111] Implement Always run the Allure renderer for 6X (#188) Bump greengage-reusable-tests-behave.yml version to v14: - Fixed a bug where the Allure report was not generated if one of the Behave tests failed. - Now all reports are generated and exported to artifacts regardless of the Behave tests completion status. Task: GG-97 --- .github/workflows/greengage-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index dd9ac9c700e7..ef90d9e371f5 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -41,7 +41,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v9 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v14 with: version: 6 target_os: ${{ matrix.target_os }} From aa125cb5ae4aa171c54130de6ec21b13857dce2d Mon Sep 17 00:00:00 2001 From: Denis Kovalev Date: Fri, 23 Jan 2026 15:11:36 +0300 Subject: [PATCH 048/111] Add gpMgmt make check to the regression tests suite (#190) Add gpMgmt `make check` to the installcheck target. It allows to run check tests as a part of common regression tests `make installcheck-world`. Also add printing output of tests to the output in case of failure. --- gpMgmt/bin/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gpMgmt/bin/Makefile b/gpMgmt/bin/Makefile index 006aa9056ef1..23acd9210709 100644 --- a/gpMgmt/bin/Makefile +++ b/gpMgmt/bin/Makefile @@ -193,9 +193,9 @@ check-regress: check: mockinstall @echo "Running pure unit and also "unit" tests that require cluster to be up..." @TMPDIR=/tmp PYTHONPATH=$(SERVER_SRC):$(SERVER_SBIN):$(PYTHONPATH):$(PYTHONSRC_INSTALL_PYTHON_PATH):$(SRC)/ext:$(SBIN_DIR):$(LIB_DIR):$(PYLIB_DIR)/mock-1.0.1 \ - gppylib/gpunit discover --verbose -s $(SRC)/gppylib -p "test_unit*.py" 2> $(SRC)/../gpMgmt_testunit_results.log 1> $(SRC)/../gpMgmt_testunit_output.log + gppylib/gpunit discover --verbose -s $(SRC)/gppylib -p "test_unit*.py" 2> $(SRC)/../gpMgmt_testunit_results.log 1> $(SRC)/../gpMgmt_testunit_output.log || (cat $(SRC)/../gpMgmt_testunit_results.log && cat $(SRC)/../gpMgmt_testunit_output.log && exit 1) @TMPDIR=/tmp PYTHONPATH=$(SERVER_SRC):$(SERVER_SBIN):$(PYTHONPATH):$(PYTHONSRC_INSTALL_PYTHON_PATH):$(SRC)/ext:$(SBIN_DIR):$(LIB_DIR):$(PYLIB_DIR)/mock-1.0.1 \ - gppylib/gpunit discover --verbose -s $(SRC)/gppylib -p "test_cluster*.py" 2>> $(SRC)/../gpMgmt_testunit_results.log 1>> $(SRC)/../gpMgmt_testunit_output.log + gppylib/gpunit discover --verbose -s $(SRC)/gppylib -p "test_cluster*.py" 2>> $(SRC)/../gpMgmt_testunit_results.log 1>> $(SRC)/../gpMgmt_testunit_output.log || (cat $(SRC)/../gpMgmt_testunit_results.log && cat $(SRC)/../gpMgmt_testunit_output.log && exit 1) unitdevel: @echo "Running pure unit tests..." @@ -209,7 +209,7 @@ installcheck-bash: ./test/suite.bash .PHONY: installcheck -installcheck: installcheck-bash +installcheck: check installcheck-bash $(MAKE) -C gpload_test $@ clean distclean: From e27c07967f6cfa832c61dce3d0008f86c977227e Mon Sep 17 00:00:00 2001 From: Maxim Michkov Date: Mon, 26 Jan 2026 11:15:41 +0300 Subject: [PATCH 049/111] Fix gp_bash_functions test (#192) The behave test gp_bash_functions was enabled, but didn't actually run any scenarios, since its tag is gp_bash_functions.sh which didn't match the filename. Change the tag to actually enable the tests. Ticket: GG-119 --- concourse/pipelines/gpdb_6X_STABLE-generated.yml | 2 +- concourse/pipelines/templates/gpdb-tpl.yml | 2 +- gpMgmt/test/behave/mgmt_utils/environment.py | 4 ++-- gpMgmt/test/behave/mgmt_utils/gp_bash_functions.feature | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/concourse/pipelines/gpdb_6X_STABLE-generated.yml b/concourse/pipelines/gpdb_6X_STABLE-generated.yml index 142ab2d0f0d5..8ce5ec700ac5 100644 --- a/concourse/pipelines/gpdb_6X_STABLE-generated.yml +++ b/concourse/pipelines/gpdb_6X_STABLE-generated.yml @@ -1884,7 +1884,7 @@ jobs: file: gpdb_src/concourse/tasks/behave_gpdb.yml image: gpdb6-rocky8-test params: - BEHAVE_FLAGS: --tags=gp_bash_functions.sh --tags=~concourse_cluster,demo_cluster + BEHAVE_FLAGS: --tags=gp_bash_functions --tags=~concourse_cluster,demo_cluster - name: gpcheckcat_rocky8 plan: diff --git a/concourse/pipelines/templates/gpdb-tpl.yml b/concourse/pipelines/templates/gpdb-tpl.yml index 4682b101786c..1919268764e8 100644 --- a/concourse/pipelines/templates/gpdb-tpl.yml +++ b/concourse/pipelines/templates/gpdb-tpl.yml @@ -18,7 +18,7 @@ 'use_concourse_cluster': true}, {'name': 'gpinitstandby', 'use_concourse_cluster': false}, - {'name': 'gp_bash_functions.sh', + {'name': 'gp_bash_functions', 'use_concourse_cluster': false}, {'name': 'gpcheckcat', 'use_concourse_cluster': true}, diff --git a/gpMgmt/test/behave/mgmt_utils/environment.py b/gpMgmt/test/behave/mgmt_utils/environment.py index b659d67b32ca..2101b478afe2 100644 --- a/gpMgmt/test/behave/mgmt_utils/environment.py +++ b/gpMgmt/test/behave/mgmt_utils/environment.py @@ -134,7 +134,7 @@ def before_scenario(context, scenario): if 'analyzedb' not in context.feature.tags: start_database_if_not_started(context) drop_database_if_exists(context, 'testdb') - if 'gp_bash_functions.sh' in context.feature.tags or 'backup_restore_bashrc' in scenario.effective_tags: + if 'gp_bash_functions' in context.feature.tags or 'backup_restore_bashrc' in scenario.effective_tags: backup_bashrc() def after_scenario(context, scenario): @@ -154,7 +154,7 @@ def after_scenario(context, scenario): And gpstart should return a return code of 0 ''') - if 'gp_bash_functions.sh' in context.feature.tags or 'backup_restore_bashrc' in scenario.effective_tags: + if 'gp_bash_functions' in context.feature.tags or 'backup_restore_bashrc' in scenario.effective_tags: restore_bashrc() # NOTE: gpconfig after_scenario cleanup is in the step `the gpconfig context is setup` diff --git a/gpMgmt/test/behave/mgmt_utils/gp_bash_functions.feature b/gpMgmt/test/behave/mgmt_utils/gp_bash_functions.feature index 35e6629020ef..01bdd3ddaa03 100644 --- a/gpMgmt/test/behave/mgmt_utils/gp_bash_functions.feature +++ b/gpMgmt/test/behave/mgmt_utils/gp_bash_functions.feature @@ -1,4 +1,4 @@ -@gp_bash_functions.sh +@gp_bash_functions Feature: gp_bash_funtions.sh unit test cases Scenario: REMOTE_EXECUTE_AND_GET_OUTPUT returns proper output when no banner is set Given a standard local demo cluster is running From 98c0cdd39595b339a9e614d0f4cc4c4900fa6b94 Mon Sep 17 00:00:00 2001 From: Georgy Shelkovy Date: Mon, 26 Jan 2026 13:42:46 +0500 Subject: [PATCH 050/111] Fix /etc/hosts file restoring (#194) Currently, the /etc/hosts file is restored using the mv -f command, but execution errors (which occurred, for example, in a Docker environment) are ignored. Do not ignore command errors and restore the file using the cp -f command. Ticket: GG-122 --- gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py index 41d188da5010..3e262af21852 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py @@ -4253,7 +4253,7 @@ def impl(context): cmd.run(validateAfter=True) hostname = cmd.get_stdout() # Update entry in current /etc/hosts file to add new host-address - cmd = Command(name='update hostlist with new hostname', cmdStr="sudo sed 's/%s/%s__1 %s/g' > /tmp/hosts; sudo cp -f /tmp/hosts /etc/hosts;rm /tmp/hosts" + cmd = Command(name='update hostlist with new hostname', cmdStr="sudo sed 's/%s/%s__1 %s/g' > /tmp/hosts && sudo cp -f /tmp/hosts /etc/hosts && rm /tmp/hosts" %(hostname, hostname, hostname)) cmd.run(validateAfter=True) @@ -4326,7 +4326,7 @@ def impl(context): @then('restore /etc/hosts file and cleanup hostlist file') @when('restore /etc/hosts file and cleanup hostlist file') def impl(context): - cmd = "sudo mv -f /tmp/hosts_orig /etc/hosts; rm -f /tmp/clusterConfigFile-1; rm -f /tmp/hostfile--1" + cmd = "sudo cp -f /tmp/hosts_orig /etc/hosts && sudo rm /tmp/hosts_orig && rm -f /tmp/clusterConfigFile-1 && rm -f /tmp/hostfile--1" context.execute_steps(u'''Then the user runs command "%s"''' % cmd) @given('create a gpcheckperf input host file') From 0977773599ee1c855a504c06cebacdda8ea4001d Mon Sep 17 00:00:00 2001 From: Georgy Shelkovy Date: Mon, 26 Jan 2026 19:33:14 +0500 Subject: [PATCH 051/111] Eliminate demo cluster creation before behave tests (#193) Currently, behave tests require explicitly creating a demo cluster and setting the PGPORT and MASTER_DATA_DIRECTORY variables before running. Create a demo cluster and set the variables before running feature tests. Previously, the init_cluster fixture was used only for the concourse cluster, which doesn't yet run on our CI. In this patch, this fixture is now used for the demo cluster, and will be used for the concourse cluster in a separate PR. Ticket: GG-120 --- ci/scripts/behave_gpdb.bash | 6 ----- gpMgmt/test/behave/mgmt_utils/environment.py | 15 +++++------ .../behave/mgmt_utils/steps/mgmt_utils.py | 27 +++++++++++++------ gpMgmt/test/behave_utils/ci/fixtures.py | 5 ++-- gpMgmt/test/behave_utils/utils.py | 10 +++---- 5 files changed, 33 insertions(+), 30 deletions(-) diff --git a/ci/scripts/behave_gpdb.bash b/ci/scripts/behave_gpdb.bash index 8857b47c6139..80e26e7784b3 100755 --- a/ci/scripts/behave_gpdb.bash +++ b/ci/scripts/behave_gpdb.bash @@ -11,8 +11,6 @@ function gen_env(){ source /usr/local/greengage-db-devel/greengage_path.sh - source gpdb_src/gpAux/gpdemo/gpdemo-env.sh - cd "\${1}/gpdb_src/gpMgmt/" BEHAVE_TAGS="${BEHAVE_TAGS}" BEHAVE_FLAGS="${BEHAVE_FLAGS}" @@ -33,10 +31,6 @@ function _main() { exit 1 fi - # Run inside a subshell so it does not pollute the environment after - # sourcing greengage_path - time (make_cluster) - time gen_env time run_test diff --git a/gpMgmt/test/behave/mgmt_utils/environment.py b/gpMgmt/test/behave/mgmt_utils/environment.py index 2101b478afe2..244b707d6af1 100644 --- a/gpMgmt/test/behave/mgmt_utils/environment.py +++ b/gpMgmt/test/behave/mgmt_utils/environment.py @@ -20,12 +20,16 @@ def before_all(context): def before_feature(context, feature): # we should be able to run gpexpand without having a cluster initialized - tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpstate', 'gpmovemirrors', - 'gpconfig', 'gpssh-exkeys', 'gpstop', 'gpinitsystem', 'cross_subnet', - 'gplogfilter'] + tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpstate', + 'gpssh-exkeys', 'gpinitsystem', 'cross_subnet'] if set(context.feature.tags).intersection(tags_to_skip): return + if not hasattr(context, "cluster_created"): + context.cluster_created = True + from test.behave_utils.ci.fixtures import init_cluster + use_fixture(init_cluster, context) + drop_database_if_exists(context, 'testdb') drop_database_if_exists(context, 'bkdb') drop_database_if_exists(context, 'fullbkdb') @@ -102,11 +106,6 @@ def before_scenario(context, scenario): scenario.skip("skipping scenario tagged with @skip") return - if "concourse_cluster" in scenario.effective_tags and not hasattr(context, "concourse_cluster_created"): - from test.behave_utils.ci.fixtures import init_cluster - context.concourse_cluster_created = True - return use_fixture(init_cluster, context) - if 'gpmovemirrors' in context.feature.tags: context.mirror_context = MirrorMgmtContext() diff --git a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py index 3e262af21852..db8eb8a94230 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py @@ -46,9 +46,7 @@ from gppylib.parseutils import canonicalize_address default_locale = None -master_data_dir = os.environ.get('MASTER_DATA_DIRECTORY') -if master_data_dir is None: - raise Exception('Please set MASTER_DATA_DIRECTORY in environment') +master_data_dir = None def show_all_installed(gphome): x = platform.linux_distribution() @@ -80,15 +78,24 @@ def create_local_demo_cluster(context, extra_config='', with_mirrors='true', wit num_primaries = os.getenv('NUM_PRIMARY_MIRROR_PAIRS', 3) os.environ['PGPORT'] = '15432' + demoDir = os.path.abspath("%s/../gpAux/gpdemo" % os.getcwd()) + global master_data_dir + master_data_dir = "%s/datadirs/qddir/demoDataDir-1" % demoDir + os.environ['MASTER_DATA_DIRECTORY'] = master_data_dir + cmd = """ cd ../gpAux/gpdemo && export DEMO_PORT_BASE={port_base} && export NUM_PRIMARY_MIRROR_PAIRS={num_primary_mirror_pairs} && + export PGPORT={pgport} && + export MASTER_DATA_DIRECTORY={master_data_dir} && export WITH_STANDBY={with_standby} && export WITH_MIRRORS={with_mirrors} && ./demo_cluster.sh -d && ./demo_cluster.sh -c && {extra_config} ./demo_cluster.sh """.format(port_base=os.getenv('PORT_BASE', 15432), + pgport=os.getenv('PGPORT', 15432), + master_data_dir=os.getenv('MASTER_DATA_DIRECTORY', master_data_dir), num_primary_mirror_pairs=num_primaries, with_mirrors=with_mirrors, with_standby=with_standby, @@ -150,10 +157,6 @@ def impl(context, checksum_toggle): @given('the cluster is generated with "{num_primaries}" primaries only') def impl(context, num_primaries): - os.environ['PGPORT'] = '15432' - demoDir = os.path.abspath("%s/../gpAux/gpdemo" % os.getcwd()) - os.environ['MASTER_DATA_DIRECTORY'] = "%s/datadirs/qddir/demoDataDir-1" % demoDir - create_local_demo_cluster(context, with_mirrors='false', with_standby='false', num_primaries=num_primaries) context.gpexpand_mirrors_enabled = False @@ -283,19 +286,27 @@ def impl(context, checksum_toggle): is_ok = False if not is_ok: - stop_database(context) + stop_database_if_started(context) os.environ['PGPORT'] = '15432' port_base = os.getenv('PORT_BASE', 15432) + demoDir = os.path.abspath("%s/../gpAux/gpdemo" % os.getcwd()) + global master_data_dir + master_data_dir = "%s/datadirs/qddir/demoDataDir-1" % demoDir + os.environ['MASTER_DATA_DIRECTORY'] = master_data_dir cmd = """ cd ../gpAux/gpdemo; \ export DEMO_PORT_BASE={port_base} && \ export NUM_PRIMARY_MIRROR_PAIRS={num_primary_mirror_pairs} && \ + export PGPORT={pgport} && + export MASTER_DATA_DIRECTORY={master_data_dir} && export WITH_MIRRORS={with_mirrors} && \ ./demo_cluster.sh -d && ./demo_cluster.sh -c && \ env EXTRA_CONFIG="HEAP_CHECKSUM={checksum_toggle}" ./demo_cluster.sh """.format(port_base=port_base, + pgport=os.getenv('PGPORT', 15432), + master_data_dir=os.getenv('MASTER_DATA_DIRECTORY', master_data_dir), num_primary_mirror_pairs=os.getenv('NUM_PRIMARY_MIRROR_PAIRS', 3), with_mirrors='true', checksum_toggle=checksum_toggle) diff --git a/gpMgmt/test/behave_utils/ci/fixtures.py b/gpMgmt/test/behave_utils/ci/fixtures.py index 6c3d55297532..f58e0661b543 100644 --- a/gpMgmt/test/behave_utils/ci/fixtures.py +++ b/gpMgmt/test/behave_utils/ci/fixtures.py @@ -4,8 +4,7 @@ @fixture def init_cluster(context): context.execute_steps(u""" - Given the database is not running - And a working directory of the test as '/tmp/concourse_cluster' + Given the database is not running And the user runs command "rm -rf ~/gpAdminLogs/gpinitsystem*" - And a cluster is created with mirrors on "cdw" and "sdw1, sdw2, sdw3" + And a standard local demo cluster is created """) diff --git a/gpMgmt/test/behave_utils/utils.py b/gpMgmt/test/behave_utils/utils.py index abe4feee8f69..6af47a0151bf 100644 --- a/gpMgmt/test/behave_utils/utils.py +++ b/gpMgmt/test/behave_utils/utils.py @@ -25,9 +25,7 @@ PARTITION_START_DATE = '2010-01-01' PARTITION_END_DATE = '2013-01-01' -master_data_dir = os.environ.get('MASTER_DATA_DIRECTORY') -if master_data_dir is None: - raise Exception('MASTER_DATA_DIRECTORY is not set') +master_data_dir = None def execute_sql(dbname, sql): @@ -185,10 +183,12 @@ def check_return_code(context, ret_code): def check_database_is_running(context): - if not 'PGPORT' in os.environ: - raise Exception('PGPORT should be set') + if not 'PGPORT' in os.environ or not 'MASTER_DATA_DIRECTORY' in os.environ: + return False pgport = int(os.environ['PGPORT']) + global master_data_dir + master_data_dir = os.environ['MASTER_DATA_DIRECTORY'] running_status = chk_local_db_running(os.environ.get('MASTER_DATA_DIRECTORY'), pgport) gpdb_running = running_status[0] and running_status[1] and running_status[2] and running_status[3] From e822ee91395d6bcd886a75b42b9a6744e8283654 Mon Sep 17 00:00:00 2001 From: Vladislav Pavlov Date: Thu, 29 Jan 2026 09:57:07 +0300 Subject: [PATCH 052/111] Always run ABI-tests for pull requests (#195) Since abi-compare status check is required for pull requests to any branch the greengage-abi-tests workflow was modified to run for all pull requests, regardless of modified paths or target branch. - Removed path-based and branch-based filters from the greengage-abi-tests workflow trigger. - The workflow is now triggered for every pull_request event on all branches. Task: [GG-118](https://tracker.yandex.ru/GG-118) --- .github/workflows/greengage-abi-tests.yml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/.github/workflows/greengage-abi-tests.yml b/.github/workflows/greengage-abi-tests.yml index 8314ab432a9b..4e07a06052ac 100644 --- a/.github/workflows/greengage-abi-tests.yml +++ b/.github/workflows/greengage-abi-tests.yml @@ -8,22 +8,7 @@ concurrency: on: workflow_dispatch: pull_request: - paths: - - 'concourse/scripts/**' - - 'src/**' - - '.github/workflows/**' - - '.github/scripts/**' - - '.abi-check/**' - - push: - branches: - - 6X_STABLE - paths: - - 'concourse/scripts/**' - - 'src/**' - - '.github/workflows/**' - - '.github/scripts/**' - - '.abi-check/**' + branches: ['**'] env: # workaround required for checkout@v3, https://github.com/actions/checkout/issues/1590 From 26f52a01a55b97392f3bcf4e365234a67914adcb Mon Sep 17 00:00:00 2001 From: Georgy Shelkovy Date: Thu, 29 Jan 2026 20:49:51 +0500 Subject: [PATCH 053/111] Use unbuffered sed (#199) The gprecoverseg utility used buffered sed to generate the progress file from Rsync. This could cause the gpstate utility to read incomplete data from it. The error manifested itself in Behave tests on a Concourse cluster in CI. Use unbuffered sed. Ticket: GG-133 --- gpMgmt/bin/gppylib/commands/unix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpMgmt/bin/gppylib/commands/unix.py b/gpMgmt/bin/gppylib/commands/unix.py index f5ddddcbcc83..2352bb60874b 100644 --- a/gpMgmt/bin/gppylib/commands/unix.py +++ b/gpMgmt/bin/gppylib/commands/unix.py @@ -580,7 +580,7 @@ def __init__(self, name, srcFile, dstFile, srcHost=None, dstHost=None, recursive # of each line and redirects it to progress_file if progress_file: cmd_tokens.append( - '2>&1 | tr "\\r" "\\n" |sed -E "/[0-9]+%/ s/$/ :{0}/" > {1}'.format(name, pipes.quote(progress_file))) + '2>&1 | tr "\\r" "\\n" |sed -u -E "/[0-9]+%/ s/$/ :{0}/" > {1}'.format(name, pipes.quote(progress_file))) cmdStr = ' '.join(cmd_tokens) cmdStr = "set -o pipefail; {}".format(cmdStr) From 4d83d0bb40ed302fdc4030824e690f0093203558 Mon Sep 17 00:00:00 2001 From: Vladislav Pavlov Date: Thu, 29 Jan 2026 23:25:25 +0300 Subject: [PATCH 054/111] Add skip tags for features, bump CI (6.x) (#205) Add skip tags for features, bump CI (6.x) The grep-based filter used to exclude certain features from the CI matrix has been removed from the CI workflow. Instead of filtering features in CI, the @skip tag is now used directly in feature files for tests that should be excluded from CI. The behave tests workflow version was bumped to use workflow without the grep-based filter. Task: GG-125 --- .github/workflows/greengage-ci.yml | 2 +- gpMgmt/test/behave/mgmt_utils/cross_subnet.feature | 2 +- gpMgmt/test/behave/mgmt_utils/gpexpand.feature | 2 +- gpMgmt/test/behave/mgmt_utils/gpperfmon.feature | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index ef90d9e371f5..225ba11ce957 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -41,7 +41,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v14 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v15 with: version: 6 target_os: ${{ matrix.target_os }} diff --git a/gpMgmt/test/behave/mgmt_utils/cross_subnet.feature b/gpMgmt/test/behave/mgmt_utils/cross_subnet.feature index 17bac22a4f81..5defa151273e 100644 --- a/gpMgmt/test/behave/mgmt_utils/cross_subnet.feature +++ b/gpMgmt/test/behave/mgmt_utils/cross_subnet.feature @@ -1,4 +1,4 @@ -@cross_subnet +@cross_subnet @skip Feature: Tests for a cross_subnet cluster Scenario: gpinitsystem works across subnets diff --git a/gpMgmt/test/behave/mgmt_utils/gpexpand.feature b/gpMgmt/test/behave/mgmt_utils/gpexpand.feature index e19a4514c158..3c11ab5efdbe 100644 --- a/gpMgmt/test/behave/mgmt_utils/gpexpand.feature +++ b/gpMgmt/test/behave/mgmt_utils/gpexpand.feature @@ -1,4 +1,4 @@ -@gpexpand +@gpexpand @skip Feature: expand the cluster by adding more segments @gpexpand_no_mirrors diff --git a/gpMgmt/test/behave/mgmt_utils/gpperfmon.feature b/gpMgmt/test/behave/mgmt_utils/gpperfmon.feature index 97900095468f..3da10b66b0dc 100644 --- a/gpMgmt/test/behave/mgmt_utils/gpperfmon.feature +++ b/gpMgmt/test/behave/mgmt_utils/gpperfmon.feature @@ -1,4 +1,4 @@ -@gpperfmon +@gpperfmon @skip Feature: gpperfmon """ Quantum controls how frequently text files get their data put in external tables. From aeacec442c9f399d595bf945d589f899ca41f7ef Mon Sep 17 00:00:00 2001 From: Alexander Kondakov Date: Fri, 30 Jan 2026 11:21:14 +0300 Subject: [PATCH 055/111] Fix resgroup bypassing when using extended protocol (#181) When querying the db via extended protocol in a batch manner, i.e. sending a message sequence like 'P' 'B' 'E' 'P' 'B' 'E' 'S', and the first query is a bypassed one (see ShouldBypassQuery()), the next queries get into the same transaction without reassigning resgroup slot properly. For next queries the StartTransaction() is not invoked, thus the AssignResGroupOnMaster() is also not called, leaving the bypassedGroup global variable initialized and giving the query wrong memory limit values. If subsequent queries are bypassed, their execution could fail with resgroup errors, since bypassed limits are usually lower than user defined ones. This patch fixes the resgroup assignment behaviour for such specific case by allocating proper resgroup slot to the transaction at first met non-bypassing query. The exec_bind_message is chosen for the hack with suggested logic, since bind calls PortalStart() and must be sent, if the query execution is expected. Patching this function allows us avoid cases when only 'P' message is sent (parse validation), and checking the resgroup here is better for slot acquisition blocking. The condition checking that transaction is marked as bypassing, but current query shouldn't bypass. If it's true bypassedGroup is reset via UnassignResGroup(), and AttachResGroupSlot() allocates the resgroup slot. resgroup.h/.c module is refactored in order to avoid re-parsing overhead during bypass checks and slot allocation. AssignResGroupOnMaster() is decomposed to use the AttachResGroupSlot() and ShouldBypassQuery() also uses separate ShouldBypassQueryFromParseTree() --- src/backend/tcop/postgres.c | 26 ++++++++++++ src/backend/utils/resgroup/resgroup.c | 59 ++++++++++++++++++--------- src/include/utils/resgroup.h | 2 + 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 8f11a794678e..0961c6a66851 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -2320,6 +2320,32 @@ exec_bind_message(StringInfo input_message) if (save_log_statement_stats) ResetUsage(); + /* + * If several queries are processed within the same transaction during + * extended protocol communication, and the first of them is a bypassing + * command, the next commands may also bypass resgroup quota assignment. + * In order to prevent that, the best option would be to reassign resgroup + * at the start of normal command until the transaction end. + */ + if (Gp_role == GP_ROLE_DISPATCH && ResGroupIsBypassed() + && IsTransactionState()) + { + MemoryContextSwitchTo(CurTransactionContext); + + List *parsetree_list = list_make1(psrc->raw_parse_tree); + + if (!ShouldBypassQueryFromParseTree(parsetree_list)) + { + elog(DEBUG1, "Upgrading from bypassed resource group at bind time for statement: %s", + psrc->query_string); + + UnassignResGroup(); + AttachResGroupSlot(); + } + + list_free(parsetree_list); + } + /* * Start up a transaction command so we can call functions etc. (Note that * this will normally change current memory context.) Nothing happens if diff --git a/src/backend/utils/resgroup/resgroup.c b/src/backend/utils/resgroup/resgroup.c index c70b60b140a9..eeae85fa6d70 100644 --- a/src/backend/utils/resgroup/resgroup.c +++ b/src/backend/utils/resgroup/resgroup.c @@ -2646,9 +2646,6 @@ ShouldUnassignResGroup(void) void AssignResGroupOnMaster(void) { - ResGroupSlotData *slot; - ResGroupInfo groupInfo; - Assert(Gp_role == GP_ROLE_DISPATCH); /* @@ -2657,6 +2654,8 @@ AssignResGroupOnMaster(void) */ if (shouldBypassQuery(debug_query_string)) { + ResGroupInfo groupInfo; + /* * Although we decide to bypass this query we should load the * memory_spill_ratio setting from the resgroup, otherwise a @@ -2703,6 +2702,20 @@ AssignResGroupOnMaster(void) return; } + AttachResGroupSlot(); +} + +/* + * Acquire a slot from the resource group and attach the process to them. + */ +void +AttachResGroupSlot(void) +{ + ResGroupSlotData *slot; + ResGroupInfo groupInfo; + + Assert(Gp_role == GP_ROLE_DISPATCH); + PG_TRY(); { do { @@ -3878,8 +3891,6 @@ shouldBypassQuery(const char *query_string) MemoryContext oldcontext = NULL; MemoryContext tmpcontext = NULL; List *parsetree_list; - ListCell *parsetree_item; - Node *parsetree; bool bypass; if (gp_resource_group_bypass) @@ -3921,7 +3932,27 @@ shouldBypassQuery(const char *query_string) /* Only bypass SET/RESET/SHOW command and SELECT with only catalog tables * for now */ - bypass = true; + bypass = ShouldBypassQueryFromParseTree(parsetree_list); + + list_free_deep(parsetree_list); + + if (tmpcontext) + MemoryContextDelete(tmpcontext); + + return bypass; +} + +/* + * Should the query bypass the resgroup assignment? + * Basically SET/SHOW and SELECT from catalog tables + * are allowed to bypass. + */ +bool +ShouldBypassQueryFromParseTree(List *parsetree_list) +{ + ListCell *parsetree_item; + Node *parsetree; + foreach(parsetree_item, parsetree_list) { parsetree = (Node *) lfirst(parsetree_item); @@ -3929,25 +3960,15 @@ shouldBypassQuery(const char *query_string) if (IsA(parsetree, SelectStmt)) { if (!shouldBypassSelectQuery(parsetree)) - { - bypass = false; - break; - } + return false; } else if (nodeTag(parsetree) != T_VariableSetStmt && nodeTag(parsetree) != T_VariableShowStmt) { - bypass = false; - break; + return false; } } - - list_free_deep(parsetree_list); - - if (tmpcontext) - MemoryContextDelete(tmpcontext); - - return bypass; + return true; } /* diff --git a/src/include/utils/resgroup.h b/src/include/utils/resgroup.h index e40a3ab74266..f2680269b5c7 100644 --- a/src/include/utils/resgroup.h +++ b/src/include/utils/resgroup.h @@ -169,6 +169,8 @@ extern void DeserializeResGroupInfo(struct ResGroupCaps *capsOut, extern bool ShouldAssignResGroupOnMaster(void); extern bool ShouldUnassignResGroup(void); +extern bool ShouldBypassQueryFromParseTree(List *parse_tree); +extern void AttachResGroupSlot(void); extern void AssignResGroupOnMaster(void); extern void UnassignResGroup(void); extern void SwitchResGroupOnSegment(const char *buf, int len); From deedbb6a0a1e2b6c3679d7e14976e6eb46e8e634 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Tue, 2 Feb 2021 10:21:47 -0800 Subject: [PATCH 056/111] Fix tautological undefined compare The compiler is right, 'this' is defined to be never null. We fixed something like this in commit b111104f820ab53e but this recently slipped through the cracks. Example error message (varies between versions and compilers): > libnaucrates/src/statistics/CBucket.cpp:453:14: error: 'this' pointer cannot be null in well-defined C++ code; comparison may be assumed to always evaluate to true [-Werror,-Wtautological-undefined-compare] (cherry picked from commit a4274d938f49e1ace083b50feaec23e0d61955e9) --- src/backend/gporca/libnaucrates/src/statistics/CBucket.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/gporca/libnaucrates/src/statistics/CBucket.cpp b/src/backend/gporca/libnaucrates/src/statistics/CBucket.cpp index 3ddd0c987455..e63d8ad09b2a 100644 --- a/src/backend/gporca/libnaucrates/src/statistics/CBucket.cpp +++ b/src/backend/gporca/libnaucrates/src/statistics/CBucket.cpp @@ -455,7 +455,6 @@ CBucket::MakeBucketCopy(CMemoryPool *mp) BOOL CBucket::Equals(const CBucket *bucket) { - GPOS_ASSERT(this != NULL); GPOS_ASSERT(bucket != NULL); if (this->GetLowerBound()->Equals(bucket->GetLowerBound()) && this->IsLowerClosed() == bucket->IsLowerClosed() && From c2eeba152ae7e22896d635479f50d3639f5bd6a8 Mon Sep 17 00:00:00 2001 From: Grigorii Heifetz Date: Mon, 2 Feb 2026 09:12:00 +0300 Subject: [PATCH 057/111] Prohibit skip-level correlated queries in Postgres planner for 7.x * ADBDEV-9025: Prohibit skip-level correlated queries in Postgres planner (#141) Prohibit skip-level correlated queries in Postgres planner What occurs? Postgres planner creates an invalid plan with skip-level correlation involving master-only and replicated tables. The plan fails to be executed during execution. Why it occurs? Skip-level correlation (at least for distributed tables) seems to be prohibited in Postgres planner due to rescanning issues that arise - they appear, because the lowest level plan is made an initplan without correct motion node applied. ORCA planner produces valid plans for such queries, but not with master-only tables - it seems to lack a feature to work with them whatsoever. How do we fix this? Let's prohibit any skip-level correlation for Postgres Legacy planner as it leads to rescan issues because of absent motions. Alternative fixes? Changing some checks for initplans creation and enabling skip-level correlation make Postgres produces correct plan - initplan is changed to subplan and correct motions are applied. Yet, one test from rangefuncs fails. So, some research is needed. Tests? Newly added tests check that ORCA is able to plan and execute simple skip-level correlated plans, whereas Postgres planner outputs an error about lack of skip-level correlation support. * Adapt tests for 7.x * Remove extra empty line from expected * Remove ticket number from comment * Fix for master-only tables, new tests * Fix skip-level condition * Add additional condition * Fix typo error * Remove obsolete issue link * Change misleading naming --------- Co-authored-by: Denis Kovalev (cherry picked from commit 623167954a2f25109c8420b4a7f659c5596fe3a8) --- src/backend/optimizer/plan/subselect.c | 53 +++++++++++-- src/include/optimizer/subselect.h | 1 + .../regress/expected/qp_correlated_query.out | 52 ++++++++----- .../qp_correlated_query_optimizer.out | 74 ++++++++++++------- src/test/regress/sql/qp_correlated_query.sql | 43 ++++++----- 5 files changed, 155 insertions(+), 68 deletions(-) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index a29407713b87..55ef62e089dc 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -420,8 +420,7 @@ get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod, } /** - * Returns true if query refers to any table on this level or on some other - * subquery level, if recursive is true. + * Returns true if query refers to a distributed or replicated table. */ bool QueryHasDistributedRelation(Query *q, bool recursive) { @@ -439,7 +438,40 @@ bool QueryHasDistributedRelation(Query *q, bool recursive) /* Really, any kind of distribution policy causes rescan issues */ if (rte->relid != InvalidOid && rte->rtekind == RTE_RELATION) + { + GpPolicy *policy = GpPolicyFetch(rte->relid); + if (GpPolicyIsPartitioned(policy) || + GpPolicyIsReplicated(policy)) + { + pfree(policy); return true; + } + pfree(policy); + } + } + return false; +} + +/** + * Returns true if query refers to an entry table. + */ +bool QueryHasMasterOnlyRelation(Query *q) { + ListCell *rt = NULL; + + foreach(rt, q->rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); + + if (rte->relid != InvalidOid + && rte->rtekind == RTE_RELATION) + { + GpPolicy *policy = GpPolicyFetch(rte->relid); + if (GpPolicyIsEntry(policy)) + { + pfree(policy); + return true; + } + } } return false; } @@ -528,9 +560,11 @@ check_multi_subquery_correlated(PlannerInfo *root, Var *var) if (list_length(root->parse->rtable) == 0) return; + PlannerInfo *parent_root = NULL; + PlannerInfo *cur_root = root; for (levelsup = var->varlevelsup; levelsup > 0; levelsup--) { - PlannerInfo *parent_root = root->parent_root; + parent_root = cur_root->parent_root; if (parent_root == NULL) elog(ERROR, "not found parent root when checking skip-level correlations"); @@ -539,16 +573,25 @@ check_multi_subquery_correlated(PlannerInfo *root, Var *var) * Only check sublink not include subquery */ if(parent_root->parse->hasSubLinks && - QueryHasDistributedRelation(root->parse, parent_root->is_correlated_subplan)) + QueryHasDistributedRelation(cur_root->parse, parent_root->is_correlated_subplan)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("correlated subquery with skip-level correlations is not supported"))); } - root = root->parent_root; + cur_root = cur_root->parent_root; } + if (parent_root->parse->hasSubLinks && + QueryHasDistributedRelation(parent_root->parse, false) && + QueryHasMasterOnlyRelation(root->parse)) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("correlated subquery with skip-level correlations is not supported"))); + } + return; } diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h index ac51a2d96c15..b713dc830214 100644 --- a/src/include/optimizer/subselect.h +++ b/src/include/optimizer/subselect.h @@ -50,6 +50,7 @@ extern void check_multi_subquery_correlated(PlannerInfo *root, Var *var); extern List *generate_subquery_vars(PlannerInfo *root, List *tlist, Index varno); extern bool QueryHasDistributedRelation(Query *q, bool recursive); +extern bool QueryHasMasterOnlyRelation(Query *q); extern bool cte_contains_dml(Node *ctequery, PlannerInfo *root); diff --git a/src/test/regress/expected/qp_correlated_query.out b/src/test/regress/expected/qp_correlated_query.out index 87e335036e85..2d5c833835e3 100644 --- a/src/test/regress/expected/qp_correlated_query.out +++ b/src/test/regress/expected/qp_correlated_query.out @@ -3911,14 +3911,14 @@ DROP TABLE skip_correlated_t4; reset optimizer_join_order; reset optimizer_trace_fallback; -------------------------------------------------------------------------------- --- ORCA should be able to plan and execute correctly one skip-level queries, but --- not with master-only tables. Postgres Legacy planner should give an error to --- any skip-level query. +-- ORCA should be able to plan and execute correctly skip-level queries, but not +-- with master-only. Postgres Legacy planner should give an error to skip-level +-- query involving distributed or replicated tables. -------------------------------------------------------------------------------- -CREATE TABLE skip_correlated_partitioned ( +CREATE TABLE skip_correlated_distributed ( a INT ) DISTRIBUTED BY (a); -INSERT INTO skip_correlated_partitioned VALUES (1), (2), (3); +INSERT INTO skip_correlated_distributed VALUES (1), (2), (3); CREATE TABLE skip_correlated_random ( b INT ) DISTRIBUTED RANDOMLY; @@ -3931,13 +3931,13 @@ INSERT INTO skip_correlated_replicated VALUES(1), (2), (3); EXPLAIN (COSTS OFF) SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) FROM skip_correlated_replicated; ERROR: correlated subquery with skip-level correlations is not supported SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) FROM skip_correlated_replicated ORDER BY a; ERROR: correlated subquery with skip-level correlations is not supported @@ -3946,40 +3946,40 @@ SELECT ( SELECT ( SELECT b FROM skip_correlated_random WHERE b = a ) -) FROM skip_correlated_partitioned; +) FROM skip_correlated_distributed; ERROR: correlated subquery with skip-level correlations is not supported SELECT ( SELECT ( SELECT b FROM skip_correlated_random WHERE b = a ) -) FROM skip_correlated_partitioned ORDER BY b; +) FROM skip_correlated_distributed ORDER BY b; ERROR: correlated subquery with skip-level correlations is not supported EXPLAIN (COSTS OFF) SELECT ( SELECT ( SELECT c FROM skip_correlated_replicated WHERE c = a ) -) FROM skip_correlated_partitioned; +) FROM skip_correlated_distributed; ERROR: correlated subquery with skip-level correlations is not supported SELECT ( SELECT ( SELECT c FROM skip_correlated_replicated WHERE c = a ) -) FROM skip_correlated_partitioned ORDER BY c; +) FROM skip_correlated_distributed ORDER BY c; ERROR: correlated subquery with skip-level correlations is not supported EXPLAIN (COSTS OFF) SELECT ( SELECT ( SELECT dbid FROM gp_segment_configuration WHERE dbid = a ) -) FROM skip_correlated_partitioned; +) FROM skip_correlated_distributed; ERROR: correlated subquery with skip-level correlations is not supported -- hard cases EXPLAIN (COSTS OFF) SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; @@ -3987,7 +3987,7 @@ ERROR: correlated subquery with skip-level correlations is not supported SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; @@ -3996,7 +3996,7 @@ EXPLAIN (COSTS OFF) SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + SELECT a FROM skip_correlated_distributed WHERE a = c and a = b ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; @@ -4004,12 +4004,30 @@ ERROR: correlated subquery with skip-level correlations is not supported SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + SELECT a FROM skip_correlated_distributed WHERE a = c and a = b ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; ERROR: correlated subquery with skip-level correlations is not supported -DROP TABLE skip_correlated_partitioned; +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT dbid FROM gp_segment_configuration WHERE dbid = numsegments LIMIT 1 + ) +) FROM gp_distribution_policy; + QUERY PLAN +----------------------------------------------------------------------------- + Seq Scan on gp_distribution_policy + SubPlan 2 + -> Result + InitPlan 1 (returns $1) + -> Limit + -> Seq Scan on gp_segment_configuration + Filter: (dbid = gp_distribution_policy.numsegments) + Optimizer: Postgres query optimizer +(8 rows) + +DROP TABLE skip_correlated_distributed; DROP TABLE skip_correlated_random; DROP TABLE skip_correlated_replicated; -------------------------------------------------------------------------------- diff --git a/src/test/regress/expected/qp_correlated_query_optimizer.out b/src/test/regress/expected/qp_correlated_query_optimizer.out index 9cdd0ff52c6a..cd49a080494a 100644 --- a/src/test/regress/expected/qp_correlated_query_optimizer.out +++ b/src/test/regress/expected/qp_correlated_query_optimizer.out @@ -4061,14 +4061,14 @@ DROP TABLE skip_correlated_t4; reset optimizer_join_order; reset optimizer_trace_fallback; -------------------------------------------------------------------------------- --- ORCA should be able to plan and execute correctly one skip-level queries, but --- not with master-only tables. Postgres Legacy planner should give an error to --- any skip-level query. +-- ORCA should be able to plan and execute correctly skip-level queries, but not +-- with master-only. Postgres Legacy planner should give an error to skip-level +-- query involving distributed or replicated tables. -------------------------------------------------------------------------------- -CREATE TABLE skip_correlated_partitioned ( +CREATE TABLE skip_correlated_distributed ( a INT ) DISTRIBUTED BY (a); -INSERT INTO skip_correlated_partitioned VALUES (1), (2), (3); +INSERT INTO skip_correlated_distributed VALUES (1), (2), (3); CREATE TABLE skip_correlated_random ( b INT ) DISTRIBUTED RANDOMLY; @@ -4081,7 +4081,7 @@ INSERT INTO skip_correlated_replicated VALUES(1), (2), (3); EXPLAIN (COSTS OFF) SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) FROM skip_correlated_replicated; QUERY PLAN @@ -4091,16 +4091,16 @@ SELECT ( -> Seq Scan on skip_correlated_replicated SubPlan 1 (slice0) -> Result - Filter: (skip_correlated_partitioned.a = skip_correlated_replicated.c) + Filter: (skip_correlated_distributed.a = skip_correlated_replicated.c) -> Materialize -> Gather Motion 3:1 (slice2; segments: 3) - -> Seq Scan on skip_correlated_partitioned + -> Seq Scan on skip_correlated_distributed Optimizer: Pivotal Optimizer (GPORCA) (10 rows) SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) FROM skip_correlated_replicated ORDER BY a; a @@ -4115,15 +4115,15 @@ SELECT ( SELECT ( SELECT b FROM skip_correlated_random WHERE b = a ) -) FROM skip_correlated_partitioned; +) FROM skip_correlated_distributed; QUERY PLAN ------------------------------------------------------------------------------------ Gather Motion 3:1 (slice2; segments: 3) -> Result - -> Seq Scan on skip_correlated_partitioned + -> Seq Scan on skip_correlated_distributed SubPlan 1 (slice2; segments: 3) -> Result - Filter: (skip_correlated_random.b = skip_correlated_partitioned.a) + Filter: (skip_correlated_random.b = skip_correlated_distributed.a) -> Materialize -> Broadcast Motion 3:3 (slice1; segments: 3) -> Seq Scan on skip_correlated_random @@ -4134,7 +4134,7 @@ SELECT ( SELECT ( SELECT b FROM skip_correlated_random WHERE b = a ) -) FROM skip_correlated_partitioned ORDER BY b; +) FROM skip_correlated_distributed ORDER BY b; b --- 1 @@ -4147,15 +4147,15 @@ SELECT ( SELECT ( SELECT c FROM skip_correlated_replicated WHERE c = a ) -) FROM skip_correlated_partitioned; +) FROM skip_correlated_distributed; QUERY PLAN ------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Result - -> Seq Scan on skip_correlated_partitioned + -> Seq Scan on skip_correlated_distributed SubPlan 1 (slice1; segments: 3) -> Seq Scan on skip_correlated_replicated - Filter: (c = skip_correlated_partitioned.a) + Filter: (c = skip_correlated_distributed.a) Optimizer: Pivotal Optimizer (GPORCA) (7 rows) @@ -4163,7 +4163,7 @@ SELECT ( SELECT ( SELECT c FROM skip_correlated_replicated WHERE c = a ) -) FROM skip_correlated_partitioned ORDER BY c; +) FROM skip_correlated_distributed ORDER BY c; c --- 1 @@ -4176,14 +4176,14 @@ SELECT ( SELECT ( SELECT dbid FROM gp_segment_configuration WHERE dbid = a ) -) FROM skip_correlated_partitioned; +) FROM skip_correlated_distributed; ERROR: correlated subquery with skip-level correlations is not supported -- hard cases EXPLAIN (COSTS OFF) SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; @@ -4196,17 +4196,17 @@ SELECT ( -> Seq Scan on skip_correlated_replicated SubPlan 1 (slice0) -> Result - Filter: (skip_correlated_partitioned.a = skip_correlated_replicated.c) + Filter: (skip_correlated_distributed.a = skip_correlated_replicated.c) -> Materialize -> Gather Motion 3:1 (slice2; segments: 3) - -> Seq Scan on skip_correlated_partitioned + -> Seq Scan on skip_correlated_distributed Optimizer: Pivotal Optimizer (GPORCA) (12 rows) SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; @@ -4221,11 +4221,11 @@ EXPLAIN (COSTS OFF) SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + SELECT a FROM skip_correlated_distributed WHERE a = c and a = b ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; - QUERY PLAN + QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Sort Sort Key: ((SubPlan 2)) @@ -4242,17 +4242,17 @@ SELECT ( -> Seq Scan on skip_correlated_random SubPlan 1 (slice0) -> Result - Filter: ((skip_correlated_partitioned.a = skip_correlated_replicated.c) AND (skip_correlated_partitioned.a = skip_correlated_random.b)) + Filter: ((skip_correlated_distributed.a = skip_correlated_replicated.c) AND (skip_correlated_distributed.a = skip_correlated_random.b)) -> Materialize -> Gather Motion 3:1 (slice3; segments: 3) - -> Seq Scan on skip_correlated_partitioned + -> Seq Scan on skip_correlated_distributed Optimizer: Pivotal Optimizer (GPORCA) (20 rows) SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + SELECT a FROM skip_correlated_distributed WHERE a = c and a = b ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; @@ -4263,7 +4263,25 @@ SELECT ( 3 (3 rows) -DROP TABLE skip_correlated_partitioned; +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT dbid FROM gp_segment_configuration WHERE dbid = numsegments LIMIT 1 + ) +) FROM gp_distribution_policy; + QUERY PLAN +----------------------------------------------------------------------------- + Seq Scan on gp_distribution_policy + SubPlan 2 + -> Result + InitPlan 1 (returns $1) + -> Limit + -> Seq Scan on gp_segment_configuration + Filter: (dbid = gp_distribution_policy.numsegments) + Optimizer: Postgres query optimizer +(8 rows) + +DROP TABLE skip_correlated_distributed; DROP TABLE skip_correlated_random; DROP TABLE skip_correlated_replicated; -------------------------------------------------------------------------------- diff --git a/src/test/regress/sql/qp_correlated_query.sql b/src/test/regress/sql/qp_correlated_query.sql index 3aa38b1715c3..76508aaa4d65 100644 --- a/src/test/regress/sql/qp_correlated_query.sql +++ b/src/test/regress/sql/qp_correlated_query.sql @@ -834,19 +834,19 @@ DROP TABLE skip_correlated_t4; reset optimizer_join_order; reset optimizer_trace_fallback; -------------------------------------------------------------------------------- --- ORCA should be able to plan and execute correctly one skip-level queries, but --- not with master-only tables. Postgres Legacy planner should give an error to --- any skip-level query. +-- ORCA should be able to plan and execute correctly skip-level queries, but not +-- with master-only. Postgres Legacy planner should give an error to skip-level +-- query involving distributed or replicated tables. -------------------------------------------------------------------------------- --start_ignore -DROP TABLE IF EXISTS skip_correlated_partitioned; +DROP TABLE IF EXISTS skip_correlated_distributed; DROP TABLE IF EXISTS skip_correlated_random; DROP TABLE IF EXISTS skip_correlated_replicated; --end_ignore -CREATE TABLE skip_correlated_partitioned ( +CREATE TABLE skip_correlated_distributed ( a INT ) DISTRIBUTED BY (a); -INSERT INTO skip_correlated_partitioned VALUES (1), (2), (3); +INSERT INTO skip_correlated_distributed VALUES (1), (2), (3); CREATE TABLE skip_correlated_random ( b INT @@ -862,12 +862,12 @@ INSERT INTO skip_correlated_replicated VALUES(1), (2), (3); EXPLAIN (COSTS OFF) SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) FROM skip_correlated_replicated; SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) FROM skip_correlated_replicated ORDER BY a; @@ -876,45 +876,45 @@ SELECT ( SELECT ( SELECT b FROM skip_correlated_random WHERE b = a ) -) FROM skip_correlated_partitioned; +) FROM skip_correlated_distributed; SELECT ( SELECT ( SELECT b FROM skip_correlated_random WHERE b = a ) -) FROM skip_correlated_partitioned ORDER BY b; +) FROM skip_correlated_distributed ORDER BY b; EXPLAIN (COSTS OFF) SELECT ( SELECT ( SELECT c FROM skip_correlated_replicated WHERE c = a ) -) FROM skip_correlated_partitioned; +) FROM skip_correlated_distributed; SELECT ( SELECT ( SELECT c FROM skip_correlated_replicated WHERE c = a ) -) FROM skip_correlated_partitioned ORDER BY c; +) FROM skip_correlated_distributed ORDER BY c; EXPLAIN (COSTS OFF) SELECT ( SELECT ( SELECT dbid FROM gp_segment_configuration WHERE dbid = a ) -) FROM skip_correlated_partitioned; +) FROM skip_correlated_distributed; -- hard cases EXPLAIN (COSTS OFF) SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c + SELECT a FROM skip_correlated_distributed WHERE a = c ) ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; @@ -923,19 +923,26 @@ EXPLAIN (COSTS OFF) SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + SELECT a FROM skip_correlated_distributed WHERE a = c and a = b ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; SELECT ( SELECT ( SELECT ( - SELECT a FROM skip_correlated_partitioned WHERE a = c and a = b + SELECT a FROM skip_correlated_distributed WHERE a = c and a = b ) as l3 FROM skip_correlated_random ORDER BY l3 LIMIT 1 ) ) AS l1 FROM skip_correlated_replicated ORDER BY l1; -DROP TABLE skip_correlated_partitioned; +EXPLAIN (COSTS OFF) +SELECT ( + SELECT ( + SELECT dbid FROM gp_segment_configuration WHERE dbid = numsegments LIMIT 1 + ) +) FROM gp_distribution_policy; + +DROP TABLE skip_correlated_distributed; DROP TABLE skip_correlated_random; DROP TABLE skip_correlated_replicated; -------------------------------------------------------------------------------- From e98277f15d0adbf33b59c5300238bdf7b84f9c9a Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 15 Jul 2025 18:11:18 -0400 Subject: [PATCH 058/111] Silence uninitialized-value warnings in compareJsonbContainers(). Because not every path through JsonbIteratorNext() sets val->type, some compilers complain that compareJsonbContainers() is comparing possibly-uninitialized values. The paths that don't set it return WJB_DONE, WJB_END_ARRAY, or WJB_END_OBJECT, so it's clear by manual inspection that the "(ra == rb)" code path is safe, and indeed we aren't seeing warnings about that. But the (ra != rb) case is much less obviously safe. In Assert-enabled builds it seems that the asserts rejecting WJB_END_ARRAY and WJB_END_OBJECT persuade gcc 15.x not to warn, which makes little sense because it's impossible to believe that the compiler can prove of its own accord that ra/rb aren't WJB_DONE here. (In fact they never will be, so the code isn't wrong, but why is there no warning?) Without Asserts, the appearance of warnings is quite unsurprising. We discussed fixing this by converting those two Asserts into pg_assume, but that seems not very satisfactory when it's so unclear why the compiler is or isn't warning: the warning could easily reappear with some other compiler version. Let's fix it in a less magical, more future-proof way by changing JsonbIteratorNext() so that it always does set val->type. The cost of that should be pretty negligible, and it makes the function's API spec less squishy. Reported-by: Erik Rijkers Author: Tom Lane Reviewed-by: Andres Freund Discussion: https://postgr.es/m/988bf1bc-3f1f-99f3-bf98-222f1cd9dc5e@xs4all.nl Discussion: https://postgr.es/m/0c623e8a204187b87b4736792398eaf1@postgrespro.ru Backpatch-through: 13 (cherry picked from commit postgres/postgres@aad1617b76aef034a27f2a52903702dc5435c422) --- src/backend/utils/adt/jsonb_util.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c index e45132229b27..d8aa4d890706 100644 --- a/src/backend/utils/adt/jsonb_util.c +++ b/src/backend/utils/adt/jsonb_util.c @@ -252,9 +252,6 @@ compareJsonbContainers(JsonbContainer *a, JsonbContainer *b) else { /* - * It's safe to assume that the types differed, and that the va - * and vb values passed were set. - * * If the two values were of the same container type, then there'd * have been a chance to observe the variation in the number of * elements/pairs (when processing WJB_BEGIN_OBJECT, say). They're @@ -748,15 +745,20 @@ JsonbIteratorInit(JsonbContainer *container) * It is our job to expand the jbvBinary representation without bothering them * with it. However, clients should not take it upon themselves to touch array * or Object element/pair buffers, since their element/pair pointers are - * garbage. Also, *val will not be set when returning WJB_END_ARRAY or - * WJB_END_OBJECT, on the assumption that it's only useful to access values - * when recursing in. + * garbage. + * + * *val is not meaningful when the result is WJB_DONE, WJB_END_ARRAY or + * WJB_END_OBJECT. However, we set val->type = jbvNull in those cases, + * so that callers may assume that val->type is always well-defined. */ JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested) { if (*it == NULL) + { + val->type = jbvNull; return WJB_DONE; + } /* * When stepping into a nested container, we jump back here to start @@ -794,6 +796,7 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested) * nesting). */ *it = freeAndGetParent(*it); + val->type = jbvNull; return WJB_END_ARRAY; } @@ -847,6 +850,7 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested) * of nesting). */ *it = freeAndGetParent(*it); + val->type = jbvNull; return WJB_END_OBJECT; } else @@ -891,8 +895,10 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested) return WJB_VALUE; } - elog(ERROR, "invalid iterator state"); - return -1; + elog(ERROR, "invalid jsonb iterator state"); + /* satisfy compilers that don't know that elog(ERROR) doesn't return */ + val->type = jbvNull; + return WJB_DONE; } /* From 417a0ade02f6a9200fe1a41838243d0d1c28cd7b Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Thu, 25 Nov 2021 14:19:22 +0100 Subject: [PATCH 059/111] Remove unneeded Python includes Inluding and has not been necessary since Python 2.4, since they are included via . Morever, is being removed in Python 3.11. So remove these includes. Reviewed-by: Tom Lane Discussion: https://www.postgresql.org/message-id/flat/84884.1637723223%40sss.pgh.pa.us (cherry picked from commit 4339e10f090ec6b5ceb727c00e7a345cc9178e1e) --- src/pl/plpython/plpython.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h index e438bc27e650..275c6f111fee 100644 --- a/src/pl/plpython/plpython.h +++ b/src/pl/plpython/plpython.h @@ -120,9 +120,6 @@ typedef int Py_ssize_t; #undef TEXTDOMAIN #define TEXTDOMAIN PG_TEXTDOMAIN("plpython") -#include -#include - /* put back our snprintf and vsnprintf */ #ifdef USE_REPL_SNPRINTF #ifdef snprintf From e02875d59f59cef586019bb55a987dcf3aa5957a Mon Sep 17 00:00:00 2001 From: Alexander Kondakov Date: Thu, 5 Feb 2026 15:13:30 +0300 Subject: [PATCH 060/111] Fix ORCA panic on nonexistent hash opfamilies. (#219) Fix ORCA panic on nonexistent hash opfamilies. Originally commit 5e04eb1 introduced opfamilies handling and tracking in CDistributionSpecHashed class. Its code contains PopulateDefaultOpfamilies() function, which fills opfamily array in cases when it's not passed in ctor. The initial idea translated by the class was that m_opfamilies must be always initialized in case of set EopttraceConsiderOpfamiliesForDistribution flag. However, some specific operations like "parallel union all" are able to work with data types, which do not have corresponding legacy opclass in GG 6. For example, these types are arrays (see get_legacy_cdbhash_opclass_for_base_type() in cdblegacyhash.c), json, etc. And in order to support them in union all cases commit 47a72cc allowed storing null m_opfamilies by removing an assert in PopulateDefaultOpfamilies(). That allowed orca to create CDistributionSpecHashedNoOp for union all without opfamilies. It worked ok since CDistributionSpecHashedNoOp has it's own matching rules and do not use opfamilies. Nevertheless 47a72cc broke the initial concept of not empty opfamilies in CDistributionSpecHashed and that made some severe errors possible to occur. In example provided in tests, ORCA faced segfault on accessing the null pointer (like in CDistributionSpecHashed::FMatchSubset()). This patch prevents invalid initialization of CDistributionSpecHashed by adding an exception raise into PopulateDefaultOpfamilies() to preserve class opfamily invariant. Since we still need specific parallel union all case to be planned, the ctors of CDistributionSpecHashed are modified to support CDistributionSpecHashedNoOp case. --- .../gpopt/base/CDistributionSpecHashed.h | 4 +-- .../gpopt/base/CDistributionSpecHashedNoOp.h | 5 +++- .../src/base/CDistributionSpecHashed.cpp | 15 ++++++----- .../src/base/CDistributionSpecHashedNoOp.cpp | 22 +++++++++++++--- .../operators/CPhysicalParallelUnionAll.cpp | 2 +- .../regress/expected/qp_orca_fallback.out | 24 +++++++++++++++++ .../expected/qp_orca_fallback_optimizer.out | 26 +++++++++++++++++++ src/test/regress/sql/qp_orca_fallback.sql | 14 ++++++++++ 8 files changed, 98 insertions(+), 14 deletions(-) diff --git a/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashed.h b/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashed.h index 3aa48cc1e532..5f25006d9d55 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashed.h +++ b/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashed.h @@ -74,12 +74,12 @@ class CDistributionSpecHashed : public CDistributionSpecRandom public: // ctor CDistributionSpecHashed(CExpressionArray *pdrgpexpr, BOOL fNullsColocated, - IMdIdArray *opfamilies = NULL); + IMdIdArray *opfamilies = NULL, BOOL noOp = false); // ctor CDistributionSpecHashed(CExpressionArray *pdrgpexpr, BOOL fNullsColocated, CDistributionSpecHashed *pdshashedEquiv, - IMdIdArray *opfamilies = NULL); + IMdIdArray *opfamilies = NULL, BOOL noOp = false); static CDistributionSpecHashed *MakeHashedDistrSpec( CMemoryPool *mp, CExpressionArray *pdrgpexpr, BOOL fNullsColocated, diff --git a/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashedNoOp.h b/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashedNoOp.h index 476c681235ca..a9883ef1f7d4 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashedNoOp.h +++ b/src/backend/gporca/libgpopt/include/gpopt/base/CDistributionSpecHashedNoOp.h @@ -12,7 +12,10 @@ namespace gpopt class CDistributionSpecHashedNoOp : public CDistributionSpecHashed { public: - CDistributionSpecHashedNoOp(CExpressionArray *pdrgpexr); + // explicitly pass opfamilies or NULL, since the default ones are not + // populated by parent ctor for NoOp cases. + CDistributionSpecHashedNoOp(CExpressionArray *pdrgpexr, + IMdIdArray *opfamilies); virtual EDistributionType Edt() const; diff --git a/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp b/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp index de10f60e2f44..5dacb859179f 100644 --- a/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp +++ b/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp @@ -16,6 +16,7 @@ #include "gpopt/base/CColRefSetIter.h" #include "gpopt/base/COptCtxt.h" #include "gpopt/base/CUtils.h" +#include "gpopt/exception.h" #include "gpopt/operators/CExpressionHandle.h" #include "gpopt/operators/CExpressionPreprocessor.h" #include "gpopt/operators/CPhysicalMotionBroadcast.h" @@ -40,7 +41,8 @@ using namespace gpopt; //--------------------------------------------------------------------------- CDistributionSpecHashed::CDistributionSpecHashed(CExpressionArray *pdrgpexpr, BOOL fNullsColocated, - IMdIdArray *opfamilies) + IMdIdArray *opfamilies, + BOOL noOp) : m_pdrgpexpr(pdrgpexpr), m_opfamilies(opfamilies), m_fNullsColocated(fNullsColocated), @@ -50,7 +52,7 @@ CDistributionSpecHashed::CDistributionSpecHashed(CExpressionArray *pdrgpexpr, GPOS_ASSERT(NULL != pdrgpexpr); GPOS_ASSERT(0 < pdrgpexpr->Size()); if (GPOS_FTRACE(EopttraceConsiderOpfamiliesForDistribution) && - NULL == opfamilies) + NULL == opfamilies && !noOp) { PopulateDefaultOpfamilies(); } @@ -68,7 +70,7 @@ CDistributionSpecHashed::CDistributionSpecHashed(CExpressionArray *pdrgpexpr, //--------------------------------------------------------------------------- CDistributionSpecHashed::CDistributionSpecHashed( CExpressionArray *pdrgpexpr, BOOL fNullsColocated, - CDistributionSpecHashed *pdshashedEquiv, IMdIdArray *opfamilies) + CDistributionSpecHashed *pdshashedEquiv, IMdIdArray *opfamilies, BOOL noOp) : m_pdrgpexpr(pdrgpexpr), m_opfamilies(opfamilies), m_fNullsColocated(fNullsColocated), @@ -78,7 +80,7 @@ CDistributionSpecHashed::CDistributionSpecHashed( GPOS_ASSERT(NULL != pdrgpexpr); GPOS_ASSERT(0 < pdrgpexpr->Size()); if (GPOS_FTRACE(EopttraceConsiderOpfamiliesForDistribution) && - NULL == opfamilies) + NULL == opfamilies && !noOp) { PopulateDefaultOpfamilies(); } @@ -119,8 +121,9 @@ CDistributionSpecHashed::PopulateDefaultOpfamilies() // For a data type the retrieved opfamily can be 'InvalidOid'. // Eg - For 'json', the distribution opfamily is 'InvalidOid'. // Using an InvalidOid can lead to crash. - m_opfamilies->Release(); - m_opfamilies = NULL; + GPOS_RAISE( + gpopt::ExmaGPOPT, gpdxl::ExmiUnexpectedOp, + GPOS_WSZ_LIT(": opfamily must exist for each hash expr")); return; } mdid_opfamily->AddRef(); diff --git a/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashedNoOp.cpp b/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashedNoOp.cpp index dbcebfedc118..8da365c67c6f 100644 --- a/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashedNoOp.cpp +++ b/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashedNoOp.cpp @@ -3,14 +3,15 @@ #include "gpopt/base/CDistributionSpecHashedNoOp.h" +#include "gpopt/exception.h" #include "gpopt/operators/CExpressionHandle.h" #include "gpopt/operators/CPhysicalMotionHashDistribute.h" using namespace gpopt; CDistributionSpecHashedNoOp::CDistributionSpecHashedNoOp( - CExpressionArray *pdrgpexpr) - : CDistributionSpecHashed(pdrgpexpr, true) + CExpressionArray *pdrgpexpr, IMdIdArray *opfamilies) + : CDistributionSpecHashed(pdrgpexpr, true, opfamilies, true) { } @@ -46,8 +47,21 @@ CDistributionSpecHashedNoOp::AppendEnforcers(CMemoryPool *mp, CExpressionArray *pdrgpexprNoOpRedistributionColumns = pdsChildHashed->Pdrgpexpr(); pdrgpexprNoOpRedistributionColumns->AddRef(); - CDistributionSpecHashedNoOp *pdsNoOp = GPOS_NEW(mp) - CDistributionSpecHashedNoOp(pdrgpexprNoOpRedistributionColumns); + + IMdIdArray *opfamilies = pdsChildHashed->Opfamilies(); + + if (GPOS_FTRACE(EopttraceConsiderOpfamiliesForDistribution)) + { + if (NULL == opfamilies) + GPOS_RAISE( + gpopt::ExmaGPOPT, gpdxl::ExmiUnexpectedOp, + GPOS_WSZ_LIT(": opfamily must exist for each hash expr")); + opfamilies->AddRef(); + } + + CDistributionSpecHashedNoOp *pdsNoOp = + GPOS_NEW(mp) CDistributionSpecHashedNoOp( + pdrgpexprNoOpRedistributionColumns, opfamilies); pexpr->AddRef(); CExpression *pexprMotion = GPOS_NEW(mp) CExpression( mp, GPOS_NEW(mp) CPhysicalMotionHashDistribute(mp, pdsNoOp), pexpr); diff --git a/src/backend/gporca/libgpopt/src/operators/CPhysicalParallelUnionAll.cpp b/src/backend/gporca/libgpopt/src/operators/CPhysicalParallelUnionAll.cpp index 77518ec1c6e5..54d3db6fe15a 100644 --- a/src/backend/gporca/libgpopt/src/operators/CPhysicalParallelUnionAll.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CPhysicalParallelUnionAll.cpp @@ -65,7 +65,7 @@ CPhysicalParallelUnionAll::PdsRequired(CMemoryPool *mp, CExpressionHandle &, pdrgpexprFakeRequestedColumns->Append(pexprScalarIdent); return GPOS_NEW(mp) - CDistributionSpecHashedNoOp(pdrgpexprFakeRequestedColumns); + CDistributionSpecHashedNoOp(pdrgpexprFakeRequestedColumns, NULL); } } diff --git a/src/test/regress/expected/qp_orca_fallback.out b/src/test/regress/expected/qp_orca_fallback.out index bf6f835de957..897d0f8e7206 100644 --- a/src/test/regress/expected/qp_orca_fallback.out +++ b/src/test/regress/expected/qp_orca_fallback.out @@ -330,3 +330,27 @@ SELECT * FROM jsonb_array_elements('["b", "a"]'::jsonb) WITH ORDINALITY; "a" | 2 (2 rows) +-- Orca should fallback if a hash distribution expression does not have an opfamily. +-- (array types does not have legacy opfamilies) +-- create table with legacy hash distr. +SET gp_use_legacy_hashops=1; +CREATE TABLE t_legacy (i INT, arr INT[]) DISTRIBUTED BY (i); +EXPLAIN (COSTS OFF) SELECT * FROM t_legacy INTERSECT ALL (SELECT * FROM t_legacy); + QUERY PLAN +------------------------------------------------------------------ + Gather Motion 3:1 (slice3; segments: 3) + -> HashSetOp Intersect All + -> Append + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: "*SELECT* 1".i, "*SELECT* 1".arr + -> Subquery Scan on "*SELECT* 1" + -> Seq Scan on t_legacy + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: "*SELECT* 2".i, "*SELECT* 2".arr + -> Subquery Scan on "*SELECT* 2" + -> Seq Scan on t_legacy t_legacy_1 + Optimizer: Postgres query optimizer +(12 rows) + +DROP TABLE t_legacy; +RESET gp_use_legacy_hashops; diff --git a/src/test/regress/expected/qp_orca_fallback_optimizer.out b/src/test/regress/expected/qp_orca_fallback_optimizer.out index 2c5854d71ef0..730f121e33ed 100644 --- a/src/test/regress/expected/qp_orca_fallback_optimizer.out +++ b/src/test/regress/expected/qp_orca_fallback_optimizer.out @@ -384,3 +384,29 @@ DETAIL: Feature not supported: WITH ORDINALITY "a" | 2 (2 rows) +-- Orca should fallback if a hash distribution expression does not have an opfamily. +-- (array types does not have legacy opfamilies) +-- create table with legacy hash distr. +SET gp_use_legacy_hashops=1; +CREATE TABLE t_legacy (i INT, arr INT[]) DISTRIBUTED BY (i); +EXPLAIN (COSTS OFF) SELECT * FROM t_legacy INTERSECT ALL (SELECT * FROM t_legacy); +INFO: GPORCA failed to produce a plan, falling back to planner +DETAIL: Unexpected Operator : opfamily must exist for each hash expr + QUERY PLAN +------------------------------------------------------------------ + Gather Motion 3:1 (slice3; segments: 3) + -> HashSetOp Intersect All + -> Append + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: "*SELECT* 1".i, "*SELECT* 1".arr + -> Subquery Scan on "*SELECT* 1" + -> Seq Scan on t_legacy + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: "*SELECT* 2".i, "*SELECT* 2".arr + -> Subquery Scan on "*SELECT* 2" + -> Seq Scan on t_legacy t_legacy_1 + Optimizer: Postgres query optimizer +(12 rows) + +DROP TABLE t_legacy; +RESET gp_use_legacy_hashops; diff --git a/src/test/regress/sql/qp_orca_fallback.sql b/src/test/regress/sql/qp_orca_fallback.sql index f587cbbd3857..c684c8c3cfcc 100644 --- a/src/test/regress/sql/qp_orca_fallback.sql +++ b/src/test/regress/sql/qp_orca_fallback.sql @@ -126,3 +126,17 @@ INSERT INTO partition_key_dropped VALUES(3); -- Orca should fallback if a function in 'from' clause uses 'WITH ORDINALITY' SELECT * FROM jsonb_array_elements('["b", "a"]'::jsonb) WITH ORDINALITY; + +-- Orca should fallback if a hash distribution expression does not have an opfamily. +-- (array types does not have legacy opfamilies) +-- create table with legacy hash distr. +-- start_ignore +DROP TABLE IF EXISTS t_legacy; +-- end_ignore +SET gp_use_legacy_hashops=1; +CREATE TABLE t_legacy (i INT, arr INT[]) DISTRIBUTED BY (i); + +EXPLAIN (COSTS OFF) SELECT * FROM t_legacy INTERSECT ALL (SELECT * FROM t_legacy); + +DROP TABLE t_legacy; +RESET gp_use_legacy_hashops; From 895124982581a3870a41bee808b460b0bb9c5498 Mon Sep 17 00:00:00 2001 From: Andrey Sokolov Date: Mon, 9 Feb 2026 12:00:20 +0300 Subject: [PATCH 061/111] Add the temp_tables_stat extension (#133) This extension is useful to find out how much disk space is occupied by temporary tables and which user created them in which session. The extension tracks the creation and deletion of temporary table files on segments. The list of files is stored in the shared memory of each segment. If memory allocated using ShmemInitStruct is not enough, the extension uses DSM. The extension adds the tts_get_seg_files function to get the tracked files, their sizes, user and session where they was created from segments. --- gpcontrib/Makefile | 7 +- gpcontrib/temp_tables_stat/Makefile | 29 + gpcontrib/temp_tables_stat/README.md | 44 + .../expected/temp_tables_stat.out | 1543 +++++++++++++++++ .../temp_tables_stat/sql/temp_tables_stat.sql | 284 +++ .../temp_tables_stat--0.1.sql | 8 + gpcontrib/temp_tables_stat/temp_tables_stat.c | 402 +++++ .../temp_tables_stat/temp_tables_stat.control | 5 + 8 files changed, 2320 insertions(+), 2 deletions(-) create mode 100644 gpcontrib/temp_tables_stat/Makefile create mode 100644 gpcontrib/temp_tables_stat/README.md create mode 100644 gpcontrib/temp_tables_stat/expected/temp_tables_stat.out create mode 100644 gpcontrib/temp_tables_stat/sql/temp_tables_stat.sql create mode 100644 gpcontrib/temp_tables_stat/temp_tables_stat--0.1.sql create mode 100644 gpcontrib/temp_tables_stat/temp_tables_stat.c create mode 100644 gpcontrib/temp_tables_stat/temp_tables_stat.control diff --git a/gpcontrib/Makefile b/gpcontrib/Makefile index fe9f05e1f260..a6c42f8aea6b 100644 --- a/gpcontrib/Makefile +++ b/gpcontrib/Makefile @@ -25,7 +25,8 @@ ifeq "$(enable_debug_extensions)" "yes" gp_percentile_agg \ gp_error_handling \ gp_subtransaction_overflow \ - gp_check_functions + gp_check_functions \ + temp_tables_stat else recurse_targets = gp_sparse_vector \ gp_distribution_policy \ @@ -37,7 +38,8 @@ else gp_percentile_agg \ gp_error_handling \ gp_subtransaction_overflow \ - gp_check_functions + gp_check_functions \ + temp_tables_stat endif ifeq "$(with_zstd)" "yes" @@ -102,4 +104,5 @@ installcheck: $(MAKE) -C gp_percentile_agg installcheck $(MAKE) -C gp_subtransaction_overflow installcheck $(MAKE) -C gp_check_functions installcheck + $(MAKE) -C temp_tables_stat installcheck diff --git a/gpcontrib/temp_tables_stat/Makefile b/gpcontrib/temp_tables_stat/Makefile new file mode 100644 index 000000000000..2476f77cc880 --- /dev/null +++ b/gpcontrib/temp_tables_stat/Makefile @@ -0,0 +1,29 @@ +# gpcontrib/temp_tables_stat/Makefile + +MODULE_big = temp_tables_stat +OBJS = $(MODULE_big).o + +EXTENSION = $(MODULE_big) +PGFILEDESC = "Collect and show statistics on temporary tables" + +DATA = $(MODULE_big)--0.1.sql + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = gpcontrib/temp_tables_stat +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + +installcheck: + cd ../../src/test/isolation2/ && \ + make install && \ + ./pg_isolation2_regress \ + $(EXTRA_REGRESS_OPTS) \ + --inputdir=${CURDIR} \ + --dbname=temp_tables_stat_isolation \ + temp_tables_stat diff --git a/gpcontrib/temp_tables_stat/README.md b/gpcontrib/temp_tables_stat/README.md new file mode 100644 index 000000000000..3df26fc25af9 --- /dev/null +++ b/gpcontrib/temp_tables_stat/README.md @@ -0,0 +1,44 @@ +# temp_tables_stat + +Sometimes users can create temporary tables which take significant disk space +and we need to know how much the space is being used in each session. +This extension is useful to find out how much disk space is occupied by +temporary tables and which user created them in which session. +The extension tracks the creation and deletion of temporary table files on +segments. The list of files is stored in the shared memory of each segment. +The extension adds the `tts_get_seg_files` function to get the tracked files, +their sizes, user and session where they were created from segments. + +Example + +``` +SELECT * FROM tts_get_seg_files(); +``` + +## How to create the extension + +Add temp_tables_stat to shared_preload_libraries and restart the cluster. + +``` +gpconfig -c shared_preload_libraries -v \ + "$(psql -At -c \ + "SELECT array_to_string( \ + array_append( \ + string_to_array( \ + current_setting('shared_preload_libraries'), \ + ','), \ + 'temp_tables_stat'), \ + ',')" \ + postgres)" +gpstop -ra +``` + +Create the extension in your database. + +``` +CREATE EXTENSION temp_tables_stat; +``` + +## Test + +src/test/isolation2/sql/temp_tables_stat.sql diff --git a/gpcontrib/temp_tables_stat/expected/temp_tables_stat.out b/gpcontrib/temp_tables_stat/expected/temp_tables_stat.out new file mode 100644 index 000000000000..33827eed198c --- /dev/null +++ b/gpcontrib/temp_tables_stat/expected/temp_tables_stat.out @@ -0,0 +1,1543 @@ + +1: CREATE OR REPLACE FUNCTION get_files (OUT user_id_ok bool, OUT cur_sess_id bool, OUT content int2, OUT size int8) RETURNS SETOF record AS $$ SELECT (SELECT a.oid = f.user_id FROM pg_authid a WHERE a.rolname = current_user) user_id_ok, (SELECT s.setting::int = f.sess_id FROM pg_settings s WHERE name = 'gp_session_id') cur_sess_id, content, size FROM tts_get_seg_files() f; -- $$ LANGUAGE SQL; +CREATE + +1: ! mkdir -p /tmp/tts_tblspace; + +1: CREATE TABLESPACE tts_tblspace LOCATION '/tmp/tts_tblspace'; +CREATE + +-- No tables, the files list is empty +1: SELECT * FROM tts_get_seg_files(); + user_id | sess_id | path | content | size +---------+---------+------+---------+------ +(0 rows) + +-- +-- Ordinary heap tables + +-- We can see tables created in the current session +1: CREATE TEMP TABLE t1(i INT) DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------ + t | t | 0 | 0 + t | t | 1 | 0 + t | t | 2 | 0 +(3 rows) +1: CREATE TEMP TABLE ts(i INT) TABLESPACE tts_tblspace DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t2(i INT) DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t3(i INT) DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------ + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 +(12 rows) +1: DROP TABLE ts; +DROP + +-- We can see tables created in other sessions +2: CREATE TEMP TABLE t1(i INT) DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------ + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | f | 1 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | f | 2 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | f | 0 | 0 +(12 rows) +2: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------ + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | t | 0 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | t | 1 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | t | 2 | 0 +(12 rows) +3: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------ + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 +(12 rows) + +-- Dropped tables are removed from the list in all sessions +1: DROP TABLE t2; +DROP +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 9 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 9 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 9 +(1 row) +2: DROP TABLE t1; +DROP +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 6 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 6 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 6 +(1 row) +1q: ... +2q: ... +3q: ... + +-- +-- Heap tables, on commit drop + +-- We can see tables created in the current session +1: BEGIN; +BEGIN +1: CREATE TEMP TABLE t1(i INT) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------ + t | t | 1 | 0 + t | t | 2 | 0 + t | t | 0 | 0 +(3 rows) +1: CREATE TEMP TABLE ts(i INT) ON COMMIT DROP TABLESPACE tts_tblspace DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t2(i INT) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t3(i INT) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------ + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 +(12 rows) + +-- We can see tables created in other sessions +2: BEGIN; +BEGIN +2: CREATE TEMP TABLE t1(i INT) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------ + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | f | 2 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | f | 1 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | f | 0 | 0 +(15 rows) +2: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------ + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | t | 2 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | t | 0 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | t | 1 | 0 +(15 rows) +3: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------ + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 +(15 rows) + +-- Dropped tables are removed from the list in all sessions +1: ROLLBACK; +ROLLBACK +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 3 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 3 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 3 +(1 row) +2: COMMIT; +COMMIT +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 0 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 0 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 0 +(1 row) +1q: ... +2q: ... +3q: ... + +-- +-- Ordinary AO tables +-- 4 files per AO table: data file, pg_aoseg, pg_aovisimap, pg_aovisimap_index + +-- We can see tables created in the current session +1: CREATE TEMP TABLE t1(i INT) WITH (APPENDOPTIMIZED = TRUE) DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 +(12 rows) +1: CREATE TEMP TABLE ts(i INT) WITH (APPENDOPTIMIZED = TRUE) TABLESPACE tts_tblspace DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t2(i INT) WITH (APPENDOPTIMIZED = TRUE) DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t3(i INT) WITH (APPENDOPTIMIZED = TRUE) DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 +(48 rows) +1: DROP TABLE ts; +DROP + + +-- We can see tables created in other sessions +2: CREATE TEMP TABLE t1(i INT) WITH (APPENDOPTIMIZED = TRUE) DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 +(48 rows) +2: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 +(48 rows) +3: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 +(48 rows) + +-- Dropped tables are removed from the list in all sessions +1: DROP TABLE t2; +DROP +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 36 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 36 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 36 +(1 row) +2: DROP TABLE t1; +DROP +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 24 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 24 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 24 +(1 row) +1q: ... +2q: ... +3q: ... + +-- +-- AO tables, on commit drop + +-- We can see tables created in the current session +1: BEGIN; +BEGIN +1: CREATE TEMP TABLE t1(i INT) WITH (APPENDOPTIMIZED = TRUE) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 +(12 rows) +1: CREATE TEMP TABLE ts(i INT) WITH (APPENDOPTIMIZED = TRUE) ON COMMIT DROP TABLESPACE tts_tblspace DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t2(i INT) WITH (APPENDOPTIMIZED = TRUE) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t3(i INT) WITH (APPENDOPTIMIZED = TRUE) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 +(48 rows) + +-- We can see tables created in other sessions +2: BEGIN; +BEGIN +2: CREATE TEMP TABLE t1(i INT) WITH (APPENDOPTIMIZED = TRUE) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 +(60 rows) +2: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 +(60 rows) +3: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 +(60 rows) + +-- Dropped tables are removed from the list in all sessions +1: ROLLBACK; +ROLLBACK +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 12 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 12 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 12 +(1 row) +2: COMMIT; +COMMIT +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 0 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 0 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 0 +(1 row) +1q: ... +2q: ... +3q: ... + +-- +-- Ordinary AOCO tables +-- 4 files per AOCO table: data file, pg_aocsseg, pg_aovisimap, pg_aovisimap_index + +-- We can see tables created in the current session +1: CREATE TEMP TABLE t1(i INT, j INT) WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 +(12 rows) +1: CREATE TEMP TABLE ts(i INT, j INT) WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) TABLESPACE tts_tblspace DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t2(i INT, j INT) WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t3(i INT, j INT) WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 +(48 rows) +1: DROP TABLE ts; +DROP + +-- We can see tables created in other sessions +2: CREATE TEMP TABLE t1(i INT, j INT) WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 +(48 rows) +2: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 +(48 rows) +3: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 +(48 rows) + +-- Dropped tables are removed from the list in all sessions +1: DROP TABLE t2; +DROP +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 36 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 36 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 36 +(1 row) +2: DROP TABLE t1; +DROP +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 24 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 24 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 24 +(1 row) +1q: ... +2q: ... +3q: ... + + +-- +-- AOCO tables, on commit drop + +-- We can see tables created in the current session +1: BEGIN; +BEGIN +1: CREATE TEMP TABLE t1(i INT, j INT) WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 +(12 rows) +1: CREATE TEMP TABLE ts(i INT, j INT) WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) ON COMMIT DROP TABLESPACE tts_tblspace DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t2(i INT, j INT) WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: CREATE TEMP TABLE t3(i INT, j INT) WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 +(48 rows) + +-- We can see tables created in other sessions +2: BEGIN; +BEGIN +2: CREATE TEMP TABLE t1(i INT, j INT) WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) ON COMMIT DROP DISTRIBUTED BY (i); +CREATE +1: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 +(60 rows) +2: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 0 + t | t | 1 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 0 + t | t | 0 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 0 + t | t | 2 | 32768 +(60 rows) +3: SELECT * FROM get_files(); + user_id_ok | cur_sess_id | content | size +------------+-------------+---------+------- + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 0 + t | f | 2 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 0 + t | f | 0 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 0 + t | f | 1 | 32768 +(60 rows) + +-- Dropped tables are removed from the list in all sessions +1: ROLLBACK; +ROLLBACK +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 12 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 12 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 12 +(1 row) +2: COMMIT; +COMMIT +1: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 0 +(1 row) +2: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 0 +(1 row) +3: SELECT COUNT(*) FROM tts_get_seg_files(); + count +------- + 0 +(1 row) +1q: ... +2q: ... +3q: ... + +-- +-- Check that files size calculation takes into account all the forks +CREATE TEMP TABLE t1 WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) AS SELECT i, i j FROM generate_series(1, 100) i DISTRIBUTED BY (i); +CREATE 100 +-- t1 consists of two colums. Both column files are taken into account +SELECT content, size FROM tts_get_seg_files(); + content | size +---------+------- + 2 | 288 + 2 | 32768 + 2 | 0 + 2 | 32768 + 0 | 384 + 0 | 32768 + 0 | 0 + 0 | 32768 + 1 | 384 + 1 | 32768 + 1 | 0 + 1 | 32768 +(12 rows) +-- Vaccum adds FSM and VM +VACUUM t1; +VACUUM +SELECT content, size FROM tts_get_seg_files(); + content | size +---------+-------- + 1 | 384 + 1 | 163840 + 1 | 0 + 1 | 32768 + 2 | 288 + 2 | 163840 + 2 | 0 + 2 | 32768 + 0 | 384 + 0 | 163840 + 0 | 0 + 0 | 32768 +(12 rows) + +-- +-- Cleanup +DROP TABLESPACE tts_tblspace; +DROP +DROP FUNCTION get_files (OUT user_id_ok bool, OUT cur_sess_id bool, OUT content int2, OUT size int8); +DROP + +DROP EXTENSION temp_tables_stat; +DROP + diff --git a/gpcontrib/temp_tables_stat/sql/temp_tables_stat.sql b/gpcontrib/temp_tables_stat/sql/temp_tables_stat.sql new file mode 100644 index 000000000000..a9de510c7d75 --- /dev/null +++ b/gpcontrib/temp_tables_stat/sql/temp_tables_stat.sql @@ -0,0 +1,284 @@ +-- start_ignore +0: ! gpconfig -c shared_preload_libraries -v "$(psql -At -c "SELECT array_to_string(array_append(string_to_array(current_setting('shared_preload_libraries'), ','), 'temp_tables_stat'), ',')" postgres)"; +0: ! gpstop -raiq; + +1: CREATE EXTENSION IF NOT EXISTS temp_tables_stat; +-- end_ignore + +1: CREATE OR REPLACE FUNCTION get_files +(OUT user_id_ok bool, OUT cur_sess_id bool, OUT content int2, OUT size int8) +RETURNS SETOF record +AS $$ + SELECT (SELECT a.oid = f.user_id + FROM pg_authid a + WHERE a.rolname = current_user) user_id_ok, + (SELECT s.setting::int = f.sess_id + FROM pg_settings s + WHERE name = 'gp_session_id') cur_sess_id, + content, + size + FROM tts_get_seg_files() f; -- +$$ LANGUAGE SQL; + +1: ! mkdir -p /tmp/tts_tblspace; +1: CREATE TABLESPACE tts_tblspace LOCATION '/tmp/tts_tblspace'; + +-- No tables, the files list is empty +1: SELECT * FROM tts_get_seg_files(); + +-- +-- Ordinary heap tables + +-- We can see tables created in the current session +1: CREATE TEMP TABLE t1(i INT) DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +1: CREATE TEMP TABLE ts(i INT) + TABLESPACE tts_tblspace + DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t2(i INT) DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t3(i INT) DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +1: DROP TABLE ts; + +-- We can see tables created in other sessions +2: CREATE TEMP TABLE t1(i INT) DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +2: SELECT * FROM get_files(); +3: SELECT * FROM get_files(); + +-- Dropped tables are removed from the list in all sessions +1: DROP TABLE t2; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +2: DROP TABLE t1; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +1q: +2q: +3q: + +-- +-- Heap tables, on commit drop + +-- We can see tables created in the current session +1: BEGIN; +1: CREATE TEMP TABLE t1(i INT) ON COMMIT DROP DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +1: CREATE TEMP TABLE ts(i INT) + ON COMMIT DROP + TABLESPACE tts_tblspace + DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t2(i INT) ON COMMIT DROP DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t3(i INT) ON COMMIT DROP DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); + +-- We can see tables created in other sessions +2: BEGIN; +2: CREATE TEMP TABLE t1(i INT) ON COMMIT DROP DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +2: SELECT * FROM get_files(); +3: SELECT * FROM get_files(); + +-- Dropped tables are removed from the list in all sessions +1: ROLLBACK; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +2: COMMIT; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +1q: +2q: +3q: + +-- +-- Ordinary AO tables +-- 4 files per AO table: data file, pg_aoseg, pg_aovisimap, pg_aovisimap_index + +-- We can see tables created in the current session +1: CREATE TEMP TABLE t1(i INT) WITH (APPENDOPTIMIZED = TRUE) DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +1: CREATE TEMP TABLE ts(i INT) + WITH (APPENDOPTIMIZED = TRUE) + TABLESPACE tts_tblspace + DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t2(i INT) WITH (APPENDOPTIMIZED = TRUE) DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t3(i INT) WITH (APPENDOPTIMIZED = TRUE) DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +1: DROP TABLE ts; + + +-- We can see tables created in other sessions +2: CREATE TEMP TABLE t1(i INT) WITH (APPENDOPTIMIZED = TRUE) DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +2: SELECT * FROM get_files(); +3: SELECT * FROM get_files(); + +-- Dropped tables are removed from the list in all sessions +1: DROP TABLE t2; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +2: DROP TABLE t1; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +1q: +2q: +3q: + +-- +-- AO tables, on commit drop + +-- We can see tables created in the current session +1: BEGIN; +1: CREATE TEMP TABLE t1(i INT) WITH (APPENDOPTIMIZED = TRUE) ON COMMIT DROP DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +1: CREATE TEMP TABLE ts(i INT) + WITH (APPENDOPTIMIZED = TRUE) + ON COMMIT DROP + TABLESPACE tts_tblspace + DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t2(i INT) WITH (APPENDOPTIMIZED = TRUE) ON COMMIT DROP DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t3(i INT) WITH (APPENDOPTIMIZED = TRUE) ON COMMIT DROP DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); + +-- We can see tables created in other sessions +2: BEGIN; +2: CREATE TEMP TABLE t1(i INT) WITH (APPENDOPTIMIZED = TRUE) ON COMMIT DROP DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +2: SELECT * FROM get_files(); +3: SELECT * FROM get_files(); + +-- Dropped tables are removed from the list in all sessions +1: ROLLBACK; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +2: COMMIT; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +1q: +2q: +3q: + +-- +-- Ordinary AOCO tables +-- 4 files per AOCO table: data file, pg_aocsseg, pg_aovisimap, pg_aovisimap_index + +-- We can see tables created in the current session +1: CREATE TEMP TABLE t1(i INT, j INT) + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +1: CREATE TEMP TABLE ts(i INT, j INT) + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + TABLESPACE tts_tblspace + DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t2(i INT, j INT) + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t3(i INT, j INT) + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +1: DROP TABLE ts; + +-- We can see tables created in other sessions +2: CREATE TEMP TABLE t1(i INT, j INT) + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +2: SELECT * FROM get_files(); +3: SELECT * FROM get_files(); + +-- Dropped tables are removed from the list in all sessions +1: DROP TABLE t2; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +2: DROP TABLE t1; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +1q: +2q: +3q: + + +-- +-- AOCO tables, on commit drop + +-- We can see tables created in the current session +1: BEGIN; +1: CREATE TEMP TABLE t1(i INT, j INT) + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + ON COMMIT DROP + DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +1: CREATE TEMP TABLE ts(i INT, j INT) + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + ON COMMIT DROP + TABLESPACE tts_tblspace + DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t2(i INT, j INT) + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + ON COMMIT DROP + DISTRIBUTED BY (i); +1: CREATE TEMP TABLE t3(i INT, j INT) + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + ON COMMIT DROP + DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); + +-- We can see tables created in other sessions +2: BEGIN; +2: CREATE TEMP TABLE t1(i INT, j INT) + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + ON COMMIT DROP + DISTRIBUTED BY (i); +1: SELECT * FROM get_files(); +2: SELECT * FROM get_files(); +3: SELECT * FROM get_files(); + +-- Dropped tables are removed from the list in all sessions +1: ROLLBACK; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +2: COMMIT; +1: SELECT COUNT(*) FROM tts_get_seg_files(); +2: SELECT COUNT(*) FROM tts_get_seg_files(); +3: SELECT COUNT(*) FROM tts_get_seg_files(); +1q: +2q: +3q: + +-- +-- Check that files size calculation takes into account all the forks +CREATE TEMP TABLE t1 + WITH (APPENDOPTIMIZED = TRUE, ORIENTATION = COLUMN) + AS SELECT i, i j FROM generate_series(1, 100) i + DISTRIBUTED BY (i); +-- t1 consists of two colums. Both column files are taken into account +SELECT content, size FROM tts_get_seg_files(); +-- Vaccum adds FSM and VM +VACUUM t1; +SELECT content, size FROM tts_get_seg_files(); + +-- +-- Cleanup +DROP TABLESPACE tts_tblspace; +DROP FUNCTION get_files +(OUT user_id_ok bool, OUT cur_sess_id bool, OUT content int2, OUT size int8); + +DROP EXTENSION temp_tables_stat; + +-- start_ignore +! gpconfig -c shared_preload_libraries -v "$(psql -At -c "SELECT array_to_string(array_remove(string_to_array(current_setting('shared_preload_libraries'), ','), 'temp_tables_stat'), ',')" postgres)"; +! gpstop -raiq; +-- end_ignore diff --git a/gpcontrib/temp_tables_stat/temp_tables_stat--0.1.sql b/gpcontrib/temp_tables_stat/temp_tables_stat--0.1.sql new file mode 100644 index 000000000000..00b9a2b7eacd --- /dev/null +++ b/gpcontrib/temp_tables_stat/temp_tables_stat--0.1.sql @@ -0,0 +1,8 @@ +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION temp_tables_stat" to load this file. \quit + +CREATE FUNCTION tts_get_seg_files(OUT user_id oid, OUT sess_id int4, OUT path text, OUT content int2, OUT size int8) +RETURNS SETOF RECORD +AS 'MODULE_PATHNAME', 'tts_get_seg_files' +LANGUAGE C +EXECUTE ON ALL SEGMENTS; diff --git a/gpcontrib/temp_tables_stat/temp_tables_stat.c b/gpcontrib/temp_tables_stat/temp_tables_stat.c new file mode 100644 index 000000000000..2814486517f1 --- /dev/null +++ b/gpcontrib/temp_tables_stat/temp_tables_stat.c @@ -0,0 +1,402 @@ +/*---------------------------------------------------------------------- + * + * Greengage Database + * + * Copyright (c) 2025 Greengage Community + * + * Greengage Database is licensed under the Apache License Version 2.0 + * (see LICENSE & NOTICE) and is based on the PostgreSQL Database + * Management System and Greenplum Database. + * + *---------------------------------------------------------------------- + */ +#include "postgres.h" + +#include + +#include "cdb/cdbvars.h" +#include "funcapi.h" +#include "pgstat.h" +#include "storage/dsm.h" +#include "storage/ipc.h" +#include "utils/builtins.h" + +PG_MODULE_MAGIC; + +void _PG_init(void); +void _PG_fini(void); + +static file_create_hook_type prev_file_create_hook = NULL; +static file_unlink_hook_type prev_file_unlink_hook = NULL; +static shmem_startup_hook_type prev_shmem_startup_hook = NULL; + +/* + * RelFileNodeBackend-s for each temp table are stored in the list of arrays. + * The list is located in shared memory. The head node of the list (TTSHeadNode) + * is allocated using ShmemInitStruct. Next nodes (TTSNode) are allocated using + * DSM. The next node is created when array of RelFileNodeBackend-s in + * the previous one is full. It is assumed that array in the head node is large + * enough to contain all RelFileNodeBackend-s in normal case and DSM is used + * very rarely. + */ + +typedef struct TTSNode +{ + dsm_handle next; /* Handle of DSM segment with the next node */ + int num; /* Number of elements in files */ + RelFileNodeBackend files[1000000]; +} TTSNode; + +typedef struct TTSHeadNode +{ + LWLock lock; + TTSNode node; +} TTSHeadNode; + +static TTSHeadNode *head = NULL; /* Head of the list */ + +/* Get next node by current one */ +static TTSNode * +next_node(const TTSNode *node) +{ + dsm_segment *dsm_seg; + + if (node->next == DSM_HANDLE_INVALID) + return NULL; + + dsm_seg = dsm_find_mapping(node->next); + if (dsm_seg == NULL) + { + dsm_seg = dsm_attach(node->next); + dsm_pin_mapping(dsm_seg); + } + + return dsm_segment_address(dsm_seg); +} + +/* + * Returns the last node or a new node if the last one is full. + * Returns NULL when no need to add rnode to the list. + */ +static TTSNode * +get_node_to_append(RelFileNodeBackend rnode) +{ + for (TTSNode *node = &head->node;; node = next_node(node)) + { + /* Don't add rnode when it exists in the list of arrays */ + for (int i = 0; i < node->num; i++) + if (RelFileNodeBackendEquals(rnode, node->files[i])) + return NULL; + + if (node->next != DSM_HANDLE_INVALID) + continue; + + /* Create a new node if the last node is full */ + if (node->num == ARRAY_SIZE(node->files)) + { + dsm_segment *next_seg = dsm_create(sizeof(TTSNode)); + dsm_pin_mapping(next_seg); + node->next = dsm_segment_handle(next_seg); + node = dsm_segment_address(next_seg); + node->next = DSM_HANDLE_INVALID; + node->num = 0; + } + + return node; + } +} + +/* + * This function is called with the same argument when each fork is created. + * Add file info to the list if it is not there. + */ +static void +tts_file_create_hook(RelFileNodeBackend rnode) +{ + TTSNode *node; + + if (prev_file_create_hook) + (*prev_file_create_hook)(rnode); + + if (!RelFileNodeBackendIsTemp(rnode) || head == NULL) + return; + + rnode.backend = MyBackendId; + + LWLockAcquire(&head->lock, LW_EXCLUSIVE); + + node = get_node_to_append(rnode); + if (node != NULL) + node->files[node->num++] = rnode; + + LWLockRelease(&head->lock); +} + +static void +delete_from_ttsnode(TTSNode *node, int index, TTSNode *prev_node) +{ + /* Find the last node */ + TTSNode *last_node = node; + TTSNode *last_prev_node = prev_node; + + while (last_node->next != DSM_HANDLE_INVALID) + { + last_prev_node = last_node; + last_node = next_node(last_node); + } + + /* replace the deleted element with the last one */ + node->files[index] = last_node->files[last_node->num - 1]; + + if (last_node->num > 1) + last_node->num--; + else if (last_node == &head->node) + head->node.num = 0; + else + { + /* + * last_prev_node != NULL because last_node is not head. + * next_node() has been called, so the mapping exists. + */ + dsm_detach(dsm_find_mapping(last_prev_node->next)); + last_prev_node->next = DSM_HANDLE_INVALID; + } +} + +/* This function is called once for all forks. Delete file info from the list */ +static void +tts_file_unlink_hook(RelFileNodeBackend rnode) +{ + if (prev_file_unlink_hook) + (*prev_file_unlink_hook)(rnode); + + if (!RelFileNodeBackendIsTemp(rnode) || head == NULL) + return; + + rnode.backend = MyBackendId; + LWLockAcquire(&head->lock, LW_EXCLUSIVE); + + /* Find rnode in the list of arrays and delete it from the list node */ + for (TTSNode *node = &head->node, *prev_node = NULL; + node != NULL; + prev_node = node, node = next_node(node)) + { + for (int i = 0; i < node->num; i++) + if (RelFileNodeBackendEquals(rnode, node->files[i])) + { + delete_from_ttsnode(node, i, prev_node); + goto lExit; + } + } + +lExit: + LWLockRelease(&head->lock); +} + +/* Postmaster creates a new shared memory space for the head node of the list */ +static void +tts_shmem_startup(void) +{ + bool found; + int tranche_id; + static LWLockTranche tranche; + + if (prev_shmem_startup_hook) + (*prev_shmem_startup_hook)(); + + head = ShmemInitStruct("temp_tables_stat", sizeof(TTSHeadNode), &found); + if (found) + return; + + tranche_id = LWLockNewTrancheId(); + tranche.name = "temp_tables_stat"; + tranche.array_base = &head->lock; + tranche.array_stride = sizeof(head->lock); + LWLockRegisterTranche(tranche_id, &tranche); + LWLockInitialize(&head->lock, tranche_id); + head->node.next = DSM_HANDLE_INVALID; + head->node.num = 0; +} + +/* + * Get size of all files from the dirname directory, which names start + * with fn_start + */ +static int64 +tts_get_file_size(const char *dirname, const char *fn_start) +{ + struct dirent *direntry; + int64 dirsize = 0; + const size_t fn_start_len = strlen(fn_start); + DIR *dirdesc = AllocateDir(dirname); + + if (!dirdesc) + return 0; + + while ((direntry = ReadDir(dirdesc, dirname)) != NULL) + { + struct stat fst; + char fn[MAXPGPATH * 2]; + + CHECK_FOR_INTERRUPTS(); + + if (strcmp(direntry->d_name, ".") == 0 || + strcmp(direntry->d_name, "..") == 0 || + strncmp(direntry->d_name, fn_start, fn_start_len) != 0) + continue; + + snprintf(fn, sizeof(fn), "%s/%s", dirname, direntry->d_name); + + if (stat(fn, &fst) < 0) + continue; + + dirsize += fst.st_size; + } + + FreeDir(dirdesc); + return dirsize; +} + +/* Copy the files info from the list to local memory */ +static RelFileNodeBackend * +get_files(uint32 *files_num) +{ + RelFileNodeBackend *files; + + *files_num = 0; + + LWLockAcquire(&head->lock, LW_SHARED); + + /* Count files of temp tables */ + for (const TTSNode *node = &head->node; node != NULL; node = next_node(node)) + *files_num += node->num; + + /* Allocate local memory for array of the files data */ + files = palloc(sizeof(*files) * (*files_num)); + + /* Combine arrays from the list nodes into one array */ + *files_num = 0; + for (const TTSNode *node = &head->node; node != NULL; node = next_node(node)) + { + RelFileNodeBackend *dst = files + (*files_num); + memcpy(dst, node->files, sizeof(*files) * node->num); + *files_num += node->num; + } + + LWLockRelease(&head->lock); + + return files; +} + +/* Get temp tables files list on segments */ +PG_FUNCTION_INFO_V1(tts_get_seg_files); +Datum +tts_get_seg_files(PG_FUNCTION_ARGS) +{ + enum { NATTR = 5 }; + + FuncCallContext *funcctx; + const PgBackendStatus *beStatus; + const RelFileNodeBackend *file; + char *sep; + char *path; + HeapTuple tuple; + Datum values[NATTR] = {0}; + bool nulls [NATTR] = {0}; + static const PgBackendStatus *beStatuses = NULL; + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + TupleDesc tupdesc; + + funcctx = SRF_FIRSTCALL_INIT(); + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + tupdesc = CreateTemplateTupleDesc(NATTR, false); + TupleDescInitEntry(tupdesc, 1, "user_id", OIDOID, -1, 0); + TupleDescInitEntry(tupdesc, 2, "sess_id", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, 3, "path", TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, 4, "content", INT2OID, -1, 0); + TupleDescInitEntry(tupdesc, 5, "size", INT8OID, -1, 0); + + funcctx->tuple_desc = BlessTupleDesc(tupdesc); + + if (head->node.num == 0) + { + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + } + + funcctx->user_fctx = get_files(&funcctx->max_calls); + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + + if (funcctx->call_cntr >= funcctx->max_calls) + SRF_RETURN_DONE(funcctx); + + if (beStatuses == NULL) + { + bool found; + Size size = mul_size(sizeof(PgBackendStatus), MaxBackends); + beStatuses = ShmemInitStruct("Backend Status Array", size, &found); + if (!found) + ereport(ERROR, (errmsg("Backend Status Array is not found"))); + } + + file = ((RelFileNodeBackend *) funcctx->user_fctx) + funcctx->call_cntr; + + beStatus = &beStatuses[file->backend - 1]; + values[0] = ObjectIdGetDatum(beStatus->st_userid); + values[1] = Int32GetDatum(beStatus->st_session_id); + path = relpathbackend(file->node, TempRelBackendId, MAIN_FORKNUM); + values[2] = CStringGetTextDatum(path); + values[3] = Int16GetDatum(GpIdentity.segindex); + sep = strrchr(path, '/'); + Assert(sep != NULL); + *sep = '\0'; + values[4] = Int64GetDatum(tts_get_file_size(path, sep + 1)); + pfree(path); + + tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); + + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); +} + +void +_PG_init(void) +{ + if (!process_shared_preload_libraries_in_progress) + { + ereport(ERROR, + (errmsg("temp_tables_stat is not in shared_preload_libraries"))); + } + + if (IS_QUERY_DISPATCHER()) + return; + + RequestAddinShmemSpace(sizeof(TTSHeadNode)); + + prev_file_create_hook = file_create_hook; + file_create_hook = tts_file_create_hook; + + prev_file_unlink_hook = file_unlink_hook; + file_unlink_hook = tts_file_unlink_hook; + + prev_shmem_startup_hook = shmem_startup_hook; + shmem_startup_hook = tts_shmem_startup; +} + +void +_PG_fini(void) +{ + if (IS_QUERY_DISPATCHER()) + return; + + file_create_hook = prev_file_create_hook; + file_unlink_hook = prev_file_unlink_hook; + shmem_startup_hook = prev_shmem_startup_hook; +} diff --git a/gpcontrib/temp_tables_stat/temp_tables_stat.control b/gpcontrib/temp_tables_stat/temp_tables_stat.control new file mode 100644 index 000000000000..b08ce3480f1e --- /dev/null +++ b/gpcontrib/temp_tables_stat/temp_tables_stat.control @@ -0,0 +1,5 @@ +# temp_tables_stat extension +comment = 'Collect and show statistics on temporary tables' +default_version = '0.1' +module_pathname = '$libdir/temp_tables_stat' +relocatable = true From 3659e950968e0032ce006ca7abe85bbbbb293dc7 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Mon, 9 Feb 2026 20:36:11 +0700 Subject: [PATCH 062/111] Don't push to DockerHub when no credentials for 6.x (#236) Bump greengage-reusable-upload to v17 * Made Login to GHCR is mandatory - failure will stop the workflow * Made Login to DockerHub is conditional: - For greengagedb/greengage - mandatory (failure will stop workflow) - For other repos - optional (failure is allowed) * Skip push to DockerHub Registry if not logged in Task: GG-128 --- .github/workflows/greengage-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index 225ba11ce957..5848e825ba52 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -113,7 +113,7 @@ jobs: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-upload.yml@v9 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-upload.yml@v17 with: version: 6 target_os: ${{ matrix.target_os }} From f7a679d24d9167877cd01c71f00527324fbdd3a2 Mon Sep 17 00:00:00 2001 From: Grigorii Heifetz Date: Tue, 10 Feb 2026 12:34:38 +0300 Subject: [PATCH 063/111] Test expression refers to not projected the nested subplan attribute What happens? ORCA's ready DXL plan fails to be converted further with an error: "DXL-to-PlStmt Translation: Attribute number *some_number* not found in project list". This happens on queries with WHERE filter. In the filter, there is a NOT IN condition between two subplans. For example of such query, please, look at the tests. Why it happens? Logical chain could be traced like this: 1. During cost-based optimization, ORCA decides to use GroupExpression of PhysicalCorrelatedInnerNLJoin type for inner subplan, due to it being more cost efficient. 2. Later, during translation of ready plan to DXL, columns needed for TestExpr of outer subplan (NOT IN condition) are determined (PdxlnQuantifiedSubplan()) - some of them must be coming from inner subplan. 3. Then, inner subplan is converted to BooleanScalarSubplan and not the usual one inside PdxlnCorrelatedNLJoin(). This happens due to it meeting needed conditions (as it is PhysicalCorrelatedInnerNLJoin and has const true as a scalar, join condition). 4. As BooleanScalarSubplan is placed inside the filter with its projection replaced with const true and outer subplan expecting to get column of inner subplan from projection or directly from subplan - the error occurs. Overall, logic on step 3 seems to have never expected, that output of subplan may be needed somwhere else, so conditions for converting it to BooleanScalarSubplan are too soft. How do we fix this? Let's additionally check, that no columns, which can potentially come from subplan, are needed on some upper level. If some of them are needed, we should not make a BooleanScalarSubplan and make usual instead. Usual subplan is then placed in place of needed col_id, so plan is converted and executed properly. Tests? Tests to check correct execution and ORCA planning of case-specific NOT IN query was added. In these tests, generated DXL plan is also checked. --------- Co-authored-by: Vasiliy Ivanov --- ...edNLJWithTrueConditionNeededProjection.mdp | 699 ++++++++++++++++++ .../src/translate/CTranslatorExprToDXL.cpp | 12 +- src/backend/gporca/server/CMakeLists.txt | 2 +- 3 files changed, 711 insertions(+), 2 deletions(-) create mode 100644 src/backend/gporca/data/dxl/minidump/CorrelatedNLJWithTrueConditionNeededProjection.mdp diff --git a/src/backend/gporca/data/dxl/minidump/CorrelatedNLJWithTrueConditionNeededProjection.mdp b/src/backend/gporca/data/dxl/minidump/CorrelatedNLJWithTrueConditionNeededProjection.mdp new file mode 100644 index 000000000000..46e96deeecba --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/CorrelatedNLJWithTrueConditionNeededProjection.mdp @@ -0,0 +1,699 @@ + + + Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=8 width=6) + -> Seq Scan on t1 (cost=0.00..431.00 rows=3 width=6) + SubPlan 1 (slice0) + -> Limit (cost=0.00..431.00 rows=3 width=4) + -> Materialize (cost=0.00..431.00 rows=3 width=4) + -> Gather Motion 3:1 (slice2; segments: 3) (cost=0.00..431.00 rows=8 width=4) + -> Seq Scan on t1 t1_1 (cost=0.00..431.00 rows=3 width=4) + SubPlan 2 (slice0) + -> Result (cost=0.00..431.00 rows=2 width=4) + Filter: ((t2.int_t2 = t1.int_t1) OR (t1.text_t1 = '0'::text)) + -> Materialize (cost=0.00..431.00 rows=2 width=4) + -> Gather Motion 3:1 (slice3; segments: 3) (cost=0.00..431.00 rows=4 width=4) + -> Seq Scan on t2 (cost=0.00..431.00 rows=2 width=4) + Optimizer: Pivotal Optimizer (GPORCA) + (16 rows) + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp b/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp index c80440d31c87..907d34ae3329 100644 --- a/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp +++ b/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp @@ -3632,10 +3632,18 @@ CTranslatorExprToDXL::PdxlnCorrelatedNLJoin( // If bar is a very small table, ORCA generates a CorrelatedInnerNLJoin with // a Const true join filter and condition 10 > bar.b is added as a filter // to the scan of bar. + // But, we should not create a Boolean subplan, if output columns are needed + // somewhere on upper levels, as filter will block them from projecting. // For the other cases function BuildSubplans generates another sublink type // for correct type of join condition. + CColRefSet *pcrsRequired = + GPOS_NEW(m_mp) CColRefSet(m_mp, *pexpr->Prpp()->PcrsRequired()); + CColRefSet *pcrsOutput = GPOS_NEW(m_mp) + CColRefSet(m_mp, *pexprInnerChild->DeriveOutputColumns()); + pcrsRequired->Intersection(pcrsOutput); if (CUtils::FScalarConstTrue(pexprScalar) && - COperator::EopPhysicalCorrelatedInnerNLJoin == op_id) + COperator::EopPhysicalCorrelatedInnerNLJoin == op_id && + 0 == pcrsRequired->Size()) { // translate relational inner child expression CDXLNode *pdxlnInnerChild = @@ -3656,6 +3664,8 @@ CTranslatorExprToDXL::PdxlnCorrelatedNLJoin( BuildSubplans(pexpr, dxl_colref_array, &pdxlnCond, pdrgpdsBaseTables, pulNonGatherMotions, pfDML); } + pcrsRequired->Release(); + pcrsOutput->Release(); // extract dxl properties from correlated join CDXLPhysicalProperties *dxl_properties = GetProperties(pexpr); diff --git a/src/backend/gporca/server/CMakeLists.txt b/src/backend/gporca/server/CMakeLists.txt index e2de25e87767..4cb17c128bae 100644 --- a/src/backend/gporca/server/CMakeLists.txt +++ b/src/backend/gporca/server/CMakeLists.txt @@ -71,7 +71,7 @@ OR-WithIsNullPred Int2Predicate IN-Numeric DeduplicatePredicates AddEqualityPredicates EqualityPredicateOverDate AddPredsInSubqueries ExtractPredicateFromDisjWithComputedColumns Join-With-Subq-Preds-1 Non-Hashjoinable-Pred Non-Hashjoinable-Pred-2 Factorized-Preds IN OR AvoidConstraintDerivationForLike NLJ-DistCol-No-Broadcast NLJ-EqAllCol-No-Broadcast NLJ-EqDistCol-InEqNonDistCol-No-Broadcast -NLJ-InEqDistCol-EqNonDistCol-Redistribute CorrelatedNLJWithTrueCondition InferPredicatesInnerOfLOJ +NLJ-InEqDistCol-EqNonDistCol-Redistribute CorrelatedNLJWithTrueCondition CorrelatedNLJWithTrueConditionNeededProjection InferPredicatesInnerOfLOJ InferredPredicatesConstraintSimplification NoPushdownPredicateWithCTEAnchor InferPredicatesForPartSQ InferPredicatesForQuantifiedSQ DoubleNDVCardinalityEquals NotWellDefinedDisjunctConjunctPredicates InferPredicatesIntFromSubquery InferPredicatesFromExistsSubquery From bab8614fa31319860f8bd0d445841f1d733e9c57 Mon Sep 17 00:00:00 2001 From: Viktor Kurilko Date: Tue, 27 Feb 2024 15:27:49 +0700 Subject: [PATCH 064/111] =?UTF-8?q?=D0=9Cake=20the=20gpexpand=20test=20mor?= =?UTF-8?q?e=20stable.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some gpexpand tests, checks if the expand is interrupted after a set time, but sometimes gpexpand may expand faster than the set time and the test will fail. This patch adds a 5-second sleep after the expansion of the first table to stimulate a long expansion without increasing the amount of data. To add sleep, a fault injector in the finish_heap_swap is used, which occurs during the expansion of the table. (cherry picked from commit 38c65a3235bf6684d7da09293112464e0a38107c) Changes from original commit: - fault injector has been changed from after_swap_relation_files to reindex_relation since swap relation is not used in this version. - string format adapted to Python 2 --- gpMgmt/test/behave/mgmt_utils/gpexpand.feature | 3 +++ gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/gpMgmt/test/behave/mgmt_utils/gpexpand.feature b/gpMgmt/test/behave/mgmt_utils/gpexpand.feature index 3c11ab5efdbe..8e1f7b319cc2 100644 --- a/gpMgmt/test/behave/mgmt_utils/gpexpand.feature +++ b/gpMgmt/test/behave/mgmt_utils/gpexpand.feature @@ -21,6 +21,7 @@ Feature: expand the cluster by adding more segments And 4000000 rows are inserted into table "expansiontest1" in schema "public" with column type list "int" And 4000000 rows are inserted into table "expansiontest2" in schema "public" with column type list "int" When the user runs gpexpand with the latest gpexpand_inputfile with additional parameters "--silent" + And add 5 seconds sleep after first table expand And the user runs gpexpand to redistribute with duration "00:00:02" Then gpexpand should print "End time reached. Stopping expansion." to stdout And verify that the cluster has 2 new segments @@ -48,6 +49,7 @@ Feature: expand the cluster by adding more segments And 4000000 rows are inserted into table "expansiontest1" in schema "public" with column type list "int" And 4000000 rows are inserted into table "expansiontest2" in schema "public" with column type list "int" When the user runs gpexpand with the latest gpexpand_inputfile with additional parameters "--silent" + And add 5 seconds sleep after first table expand When the user runs gpexpand to redistribute with duration "00:00:02" Then gpexpand should print "End time reached. Stopping expansion." to stdout @@ -70,6 +72,7 @@ Feature: expand the cluster by adding more segments And 4000000 rows are inserted into table "expansiontest1" in schema "public" with column type list "int" And 4000000 rows are inserted into table "expansiontest2" in schema "public" with column type list "int" When the user runs gpexpand with the latest gpexpand_inputfile with additional parameters "--silent" + And add 5 seconds sleep after first table expand And the user runs gpexpand to redistribute with the --end flag Then gpexpand should print "End time reached. Stopping expansion." to stdout And verify that the cluster has 2 new segments diff --git a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py index db8eb8a94230..03c0322e79b6 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py @@ -4562,6 +4562,15 @@ def impl(context, logdir, stage): if attempt == num_retries: raise Exception('Timed out after {} retries'.format(num_retries)) +@when('add {seconds} seconds sleep after first table expand') +def impl(context, seconds): + create_fault_query = "CREATE EXTENSION IF NOT EXISTS gp_inject_fault;" + execute_sql(context.dbname, create_fault_query) + # We use the reindex_relation fault injector to simulate a long table + # expansion time because during the expansion of the table, we reindex + # the relation files. + inject_fault_query = "SELECT gp_inject_fault('reindex_relation', 'sleep', '', '', '', 1, 1, {}, 2);".format(seconds) + execute_sql(context.dbname, inject_fault_query) def verify_elements_in_file(filename, elements): with open(filename, 'r') as file: From 8a82795af70ada75b2dd1554b3eb484160a5e294 Mon Sep 17 00:00:00 2001 From: Viktor Kurilko Date: Wed, 28 Feb 2024 19:22:29 +0700 Subject: [PATCH 065/111] Revert "gpexpand: improve timeout tests" Commit 19d21fe7a7e adds sleep after table expansion to simulate a long expansion. Therefore, there is no longer a need to use such a volume of data. This reverts commit 96e65e5. (cherry picked from commit arenadata/gpdb@879e938) --- gpMgmt/test/behave/mgmt_utils/gpexpand.feature | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gpMgmt/test/behave/mgmt_utils/gpexpand.feature b/gpMgmt/test/behave/mgmt_utils/gpexpand.feature index 8e1f7b319cc2..9a5420311c25 100644 --- a/gpMgmt/test/behave/mgmt_utils/gpexpand.feature +++ b/gpMgmt/test/behave/mgmt_utils/gpexpand.feature @@ -17,9 +17,7 @@ Feature: expand the cluster by adding more segments When the user runs gpexpand interview to add 2 new segment and 0 new host "ignored.host" Then the number of segments have been saved And user has created expansiontest tables - And 4000000 rows are inserted into table "expansiontest0" in schema "public" with column type list "int" And 4000000 rows are inserted into table "expansiontest1" in schema "public" with column type list "int" - And 4000000 rows are inserted into table "expansiontest2" in schema "public" with column type list "int" When the user runs gpexpand with the latest gpexpand_inputfile with additional parameters "--silent" And add 5 seconds sleep after first table expand And the user runs gpexpand to redistribute with duration "00:00:02" @@ -45,9 +43,7 @@ Feature: expand the cluster by adding more segments And the cluster is setup for an expansion on hosts "cdw,sdw1" When the user runs gpexpand interview to add 2 new segment and 0 new host "ignored.host" Then user has created expansiontest tables - And 4000000 rows are inserted into table "expansiontest0" in schema "public" with column type list "int" And 4000000 rows are inserted into table "expansiontest1" in schema "public" with column type list "int" - And 4000000 rows are inserted into table "expansiontest2" in schema "public" with column type list "int" When the user runs gpexpand with the latest gpexpand_inputfile with additional parameters "--silent" And add 5 seconds sleep after first table expand When the user runs gpexpand to redistribute with duration "00:00:02" @@ -68,9 +64,7 @@ Feature: expand the cluster by adding more segments When the user runs gpexpand interview to add 2 new segment and 0 new host "ignored.host" Then the number of segments have been saved And user has created expansiontest tables - And 4000000 rows are inserted into table "expansiontest0" in schema "public" with column type list "int" And 4000000 rows are inserted into table "expansiontest1" in schema "public" with column type list "int" - And 4000000 rows are inserted into table "expansiontest2" in schema "public" with column type list "int" When the user runs gpexpand with the latest gpexpand_inputfile with additional parameters "--silent" And add 5 seconds sleep after first table expand And the user runs gpexpand to redistribute with the --end flag From e9f1a9229db0363e4f9c0b0b01e1705bdcacdc1c Mon Sep 17 00:00:00 2001 From: Georgy Shelkovy Date: Mon, 9 Feb 2026 11:45:10 +0500 Subject: [PATCH 066/111] Fix regression in multi-host cluster in behave tests Commit 0977773 introduced a regression in the behave tests, which manifests itself in gpexpand tests. Explicitly set the port in an environment variable. Move the skip tag from the entire gpexpand feature to a single scenario that requires the file /home/gpadmin/sqldump/dump.sql to run. --- gpMgmt/test/behave/mgmt_utils/gpexpand.feature | 3 ++- gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/gpMgmt/test/behave/mgmt_utils/gpexpand.feature b/gpMgmt/test/behave/mgmt_utils/gpexpand.feature index 9a5420311c25..0da5e78ee59a 100644 --- a/gpMgmt/test/behave/mgmt_utils/gpexpand.feature +++ b/gpMgmt/test/behave/mgmt_utils/gpexpand.feature @@ -1,4 +1,4 @@ -@gpexpand @skip +@gpexpand Feature: expand the cluster by adding more segments @gpexpand_no_mirrors @@ -363,6 +363,7 @@ Feature: expand the cluster by adding more segments When the user runs gpexpand to redistribute Then the numsegments of table "ext_test" is 4 + @skip @gpexpand_icw_db_concourse Scenario: Use a dump of the ICW database for expansion Given the database is not running diff --git a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py index 03c0322e79b6..7c3542ddda3b 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py @@ -3128,6 +3128,7 @@ def _create_cluster(context, master_host, segment_host_list, hba_hostnames='0', global master_data_dir master_data_dir = os.path.join(context.working_directory, 'data/master/gpseg-1') os.environ['MASTER_DATA_DIRECTORY'] = master_data_dir + os.environ['PGPORT'] = '10300' try: with dbconn.connect(dbconn.DbURL(dbname='template1'), unsetSearchPath=False) as conn: From 160067528818fa533b2ae0e1cca7050a6d6d7e37 Mon Sep 17 00:00:00 2001 From: Vladislav Pavlov Date: Wed, 11 Feb 2026 16:28:28 +0300 Subject: [PATCH 067/111] Bump behave tests workflow version (6.x) (#243) - greengage-reusable-tests-behave worflow was bumped to v18; - behave_collect_logs.bash script was added to collect behave tests logs in CI; - a new condition was added to run_behave_tests.bash to collect logs only in the CI runtime; - docker-compose.yaml was refactored: x-common-service section was added, bind mounts were added for logs in sdw2, sdw3 services; - the version was removed as docker compose now automatically uses the latest specification. --- .github/workflows/greengage-ci.yml | 2 +- ci/docker-compose.yaml | 76 ++++++++++------------------- ci/scripts/behave_collect_logs.bash | 13 +++++ ci/scripts/run_behave_tests.bash | 16 ++++-- 4 files changed, 54 insertions(+), 53 deletions(-) create mode 100755 ci/scripts/behave_collect_logs.bash diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index 5848e825ba52..1878182f923d 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -41,7 +41,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v15 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v18 with: version: 6 target_os: ${{ matrix.target_os }} diff --git a/ci/docker-compose.yaml b/ci/docker-compose.yaml index 550e24b28d24..880da9b623fb 100644 --- a/ci/docker-compose.yaml +++ b/ci/docker-compose.yaml @@ -1,64 +1,42 @@ ---- +x-common-service: &common-service + image: "${IMAGE}" + privileged: true + sysctls: + kernel.sem: 500 1024000 200 4096 + init: true + ulimits: + nofile: 65535 + entrypoint: > + sleep infinity -version: "3" services: cdw: - image: "${IMAGE}" + <<: *common-service working_dir: /home/gpadmin hostname: cdw volumes: - - "$PWD/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa" - - "$PWD/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub" - - "$PWD/allure-results:/tmp/allure-results" - - "$PWD/logs_cdw:/logs" - privileged: true - sysctls: - kernel.sem: 500 1024000 200 4096 - init: true - ulimits: - nofile: 65535 - entrypoint: > - sleep infinity + - ${PWD}/allure-results:/tmp/allure-results + - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub + - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/logs_cdw:/logs sdw1: - image: "${IMAGE}" - privileged: true + <<: *common-service hostname: sdw1 volumes: - - "$PWD/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa" - - "$PWD/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub" - - "$PWD/logs_sdw1:/logs" - sysctls: - kernel.sem: 500 1024000 200 4096 - init: true - ulimits: - nofile: 65535 - entrypoint: > - sleep infinity + - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub + - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/logs_sdw1:/logs sdw2: - image: "${IMAGE}" - privileged: true + <<: *common-service hostname: sdw2 volumes: - - "$PWD/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa" - - "$PWD/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub" - sysctls: - kernel.sem: 500 1024000 200 4096 - init: true - ulimits: - nofile: 65535 - entrypoint: > - sleep infinity + - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub + - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/logs_sdw2:/logs sdw3: - image: "${IMAGE}" - privileged: true + <<: *common-service hostname: sdw3 volumes: - - "$PWD/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa" - - "$PWD/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub" - sysctls: - kernel.sem: 500 1024000 200 4096 - init: true - ulimits: - nofile: 65535 - entrypoint: > - sleep infinity + - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub + - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/logs_sdw3:/logs diff --git a/ci/scripts/behave_collect_logs.bash b/ci/scripts/behave_collect_logs.bash new file mode 100755 index 000000000000..16064acb744d --- /dev/null +++ b/ci/scripts/behave_collect_logs.bash @@ -0,0 +1,13 @@ +feature=$1 + +params=( + "./ d gpAdminLogs" + "gpdb_src/gpAux/gpdemo/datadirs/ d log" + "gpdb_src/gpAux/gpdemo/datadirs/ d pg_log" +) +for param in "${params[@]}"; do + read -r path type name <<< "$param" + [ -d "$path" ] && find "$path" -name "$name" -type "$type" \ + -exec tar -rf "/logs/behave_${feature}_${name}.tar" "{}" \; +done +chmod -R a+rwX /logs diff --git a/ci/scripts/run_behave_tests.bash b/ci/scripts/run_behave_tests.bash index 803b3aad376d..5bce1599cdb3 100755 --- a/ci/scripts/run_behave_tests.bash +++ b/ci/scripts/run_behave_tests.bash @@ -8,6 +8,8 @@ behave_tests_dir="gpMgmt/test/behave/mgmt_utils" clusters="~concourse_cluster" +docker_compose_path="ci/docker-compose.yaml" + if [ $# -eq 0 ] then # TODO cross_subnet and gpssh tests are excluded @@ -47,7 +49,7 @@ run_feature() { echo "Started $feature behave tests on cluster $cluster and project $project" bash ci/scripts/init_containers.sh $project - docker compose -p $project -f ci/docker-compose.yaml exec -T \ + docker compose -p $project -f "$docker_compose_path" exec -T \ -e FEATURE="$feature" -e BEHAVE_FLAGS="--tags $feature --tags=$cluster \ -f behave_utils.ci.formatter:CustomFormatter \ -o non-existed-output \ @@ -56,9 +58,17 @@ run_feature() { cdw gpdb_src/ci/scripts/behave_gpdb.bash status=$? - docker compose -p $project -f ci/docker-compose.yaml --env-file ci/.env down -v + if [ -n "$CI" ]; then + local services=$(docker compose -p $project -f "$docker_compose_path" config --services | tr '\n' ' ') + for service in $services; do + docker compose -p $project -f "$docker_compose_path" exec -T \ + $service /bin/bash -s "$feature" < ./ci/scripts/behave_collect_logs.bash + done + fi + + docker compose -p $project -f "$docker_compose_path" --env-file ci/.env down -v - if [[ $status > 0 ]]; then echo "Feature $feature failed with exit code $status"; fi + if [[ $status -gt 0 ]]; then echo "Feature $feature failed with exit code $status"; fi exit $status } From f1208e86d79edfe152e77ef00aa3936cd3b5891e Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Fri, 13 Feb 2026 12:36:56 +0700 Subject: [PATCH 068/111] Collect SQL dump after new image built on main branches (#235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Сreates a new workflow `Greengage SQL Dump` in the `main` branch A separate workflow `Greengage SQL Dump` is responsible for generating SQL dump artifacts after the main CI process completes successfully. It is triggered automatically upon the completion of the `Greengage CI` workflow. Triggers: `workflow_run: workflows: ["Greengage CI"], types: [completed]` Branch Targeting: Runs only for the `main` and `7.x` branches. Version Detection: Automatically determines the database version (6 or 7) based on the triggering branch. Artifact Creation: Executes regression tests with the `dump_db: "true"` parameter to generate a SQL dump archive, which is then uploaded as a workflow artifact. Task: ADBDEV-9165 --- .github/workflows/README.md | 43 ++++++++++++- .github/workflows/greengage-sql-dump.yml | 81 ++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/greengage-sql-dump.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 1d93b3ba455e..08d466d88685 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -39,7 +39,7 @@ operating systems: ## Release Workflow -A separate workflow, `Greengage release`, handles the uploading of Debian packages +A separate workflow `Greengage release` handles the uploading of Debian package to GitHub releases. It is triggered when a release is published and uses a composite action to manage package deployment. @@ -72,6 +72,47 @@ release. The release workflow is designed to be robust and provide clear feedback when issues occur, ensuring that releases are always consistent and reliable. +## SQL Dump Workflow + +A separate workflow `Greengage SQL Dump` is responsible for generating SQL dump +artifacts after the main CI process completes successfully. It is triggered +automatically upon the completion of the `Greengage CI` workflow. + +### Key Features + +- **Triggers:** `workflow_run: workflows: ["Greengage CI"], types: [completed]` +- **Branch Targeting:** Runs only for the `main` and `7.x` branches. +- **Version Detection:** Automatically determines the database version (6 or 7) +based on the triggering branch. +- **Artifact Creation:** Executes regression tests with the `dump_db: "true"` +parameter to generate a SQL dump archive, which is then uploaded as a workflow +artifact. +- **Controlled Execution:** Since the main CI workflow runs on `main` and `7.x` +branches only for push events (which occur after final merge of a PR), SQL dump +are generated exclusively for verified, approved patches after they are merged +into the main branches. +- **Artifact Retention:** The generated SQL dump artifact is retained 90 days +after the last download. Each new run of the `behave tests gpexpand` workflow +(which consumes this artifact as a consumer) resets this retention period to +90 days when it downloads the artifact. + +### Behavior + +1. **Triggering:** Automatically starts after the `Greengage CI` workflow +finishes on the `main` or `7.x` branch. +2. **Preparation:** Configures Docker storage on the runner to utilize +`/mnt/docker` for increased disk space. +3. **Version Mapping:** Maps the branch name (`main` -> version 6, `7.x` -> +version 7) to select the correct Docker image for testing. +4. **Dump Generation:** Runs the regression test suite using the reusable +action with the `dump_db` option enabled, which creates a +`*_postgres_sqldump.tar` file. +5. **Artifact Upload:** Uploads the generated SQL dump archive as a named +artifact (e.g., `sqldump_ggdb7_ubuntu`) to the workflow run. + +This workflow ensures that a current database schema dump is available as an +artifact following successful CI runs on the primary branches `main` and `7.x`. + ## Configuration The workflow is parameterized to support flexibility: diff --git a/.github/workflows/greengage-sql-dump.yml b/.github/workflows/greengage-sql-dump.yml new file mode 100644 index 000000000000..40d1fcab67c4 --- /dev/null +++ b/.github/workflows/greengage-sql-dump.yml @@ -0,0 +1,81 @@ +# .github/workflows/greengage-sql-dump.yml +name: Greengage SQL Dump + +on: + workflow_run: + workflows: ["Greengage CI"] + types: [completed] + branches: + - main + - 7.x + +jobs: + create-sql-dump-regression: + runs-on: ubuntu-latest + timeout-minutes: 180 + if: | + github.event.workflow_run.event == 'push' && + github.event.workflow_run.conclusion == 'success' && + !startsWith(github.event.workflow_run.head_branch, 'refs/tags/') + strategy: + fail-fast: true + matrix: + target_os: [ubuntu] + permissions: + contents: read # Explicit for default behavior + packages: read # Explicit for GHCR access clarity + actions: write # Required for cache and artifact upload + steps: + - name: Display trigger information + run: | + echo "" + echo "================== Workflow Trigger Information ==================" + echo "Triggered by: ${{ github.event.workflow_run.name }} (#${{ github.event.workflow_run.id }})" + echo "Target branch: ${{ github.event.workflow_run.head_branch }}" + echo "Source commit: ${{ github.event.workflow_run.head_sha }}" + echo "Commit message: ${{ github.event.workflow_run.head_commit.message }}" + echo "" + echo "Author: ${{ github.event.workflow_run.head_commit.author.name }}" + echo "==================================================================" + + - name: Move Docker storage to /mnt/docker + run: | + sudo bash -c ' + systemctl stop docker + mv /var/lib/docker /mnt/docker + echo "{ \"data-root\": \"/mnt/docker\" }" > /etc/docker/daemon.json + systemctl start docker + ' + + - name: Determine version from branch + id: version + env: + BRANCH: ${{ github.event.workflow_run.head_branch }} + run: | + case "$BRANCH" in + main) + echo "version=6" >> $GITHUB_OUTPUT + ;; + 7.x) + echo "version=7" >> $GITHUB_OUTPUT + ;; + *) + echo "Unsupported branch: $BRANCH" + exit 1 + ;; + esac + + - name: Create SQL Dump via regression tests + uses: greengagedb/greengage-ci/.github/actions/tests/regression@v16 + with: + image: ghcr.io/${{ github.repository }}/ggdb${{ steps.version.outputs.version }}_${{ matrix.target_os }}:${{ github.event.workflow_run.head_sha }} + optimizer: postgres + target_os: ${{ matrix.target_os }} + dump_db: "true" + + - name: Upload SQL Dump + uses: actions/upload-artifact@v4 + with: + name: sqldump_ggdb${{ steps.version.outputs.version }}_${{ matrix.target_os }} + path: /mnt/logs/${{ matrix.target_os }}_postgres_sqldump.tar + if-no-files-found: error From 4c8fc9fa8a1a4b4129d663896f61970748a40c21 Mon Sep 17 00:00:00 2001 From: Maxim Michkov Date: Fri, 13 Feb 2026 13:33:36 +0300 Subject: [PATCH 069/111] Fix the use of errno as an error indicator (#248) Most library functions have the following convention: if an error happens, errno is set and some return code is returned, and if the operation completes successfully, errno is preserved. This means the correct way to handle errors is to check the return code, and if an error happens, errno is guaranteed to be set. However, some functions have a different convention. These functions are fixed in this patch: 1. readdir() returns NULL both on an error condition, and on successfully reaching the end of the directory. errno must be checked to determine if an error happened, so you have to set errno to 0 before using readdir() in all cases. 2. read() and write() return -1 on error condition and guarantee that errno is set in this case. However, they might also do a partial read/write and errno isn't guaranteed to be set. So in all cases when we check partial read/write to report an error, we must set errno to 0 before read()/write(). 3. BIO_write() from OpenSSL might return a negative value without setting errno. We should set it to 0 for it to have a defined value before the call. Ticket: GG-178 --- src/backend/access/transam/xlog.c | 1 + src/backend/executor/nodeShareInputScan.c | 2 ++ src/backend/libpq/pqcomm.c | 7 +++++++ src/backend/replication/logical/snapbuild.c | 1 + src/backend/replication/slot.c | 1 + src/backend/utils/cache/relmapper.c | 1 + src/bin/gpfdist/gpfdist.c | 1 + src/bin/initdb/initdb.c | 8 ++++---- src/bin/pg_controldata/pg_controldata.c | 1 + src/bin/pg_rewind/file_ops.c | 1 + src/bin/pg_rewind/parsexlog.c | 1 + src/interfaces/libpq/fe-lobj.c | 1 + 12 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 70becd09f32f..9818512b650e 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -12100,6 +12100,7 @@ XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen, goto next_record_is_invalid; } + errno = 0; if (read(readFile, readBuf, XLOG_BLCKSZ) != XLOG_BLCKSZ) { char fname[MAXFNAMELEN]; diff --git a/src/backend/executor/nodeShareInputScan.c b/src/backend/executor/nodeShareInputScan.c index 0a5f19da9ef4..f8e911020256 100644 --- a/src/backend/executor/nodeShareInputScan.c +++ b/src/backend/executor/nodeShareInputScan.c @@ -531,6 +531,7 @@ static int retry_read(int fd, char *buf, int rsize) Assert(rsize > 0); read_retry: + errno = 0; sz = read(fd, buf, rsize); if (sz > 0) return sz; @@ -549,6 +550,7 @@ static int retry_write(int fd, char *buf, int wsize) Assert(wsize > 0); write_retry: + errno = 0; sz = write(fd, buf, wsize); if(sz > 0) return sz; diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index 2b0700814491..553aecac2a22 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -998,6 +998,13 @@ pq_waitForDataUsingSelect(void) errno = 0; numSockets = select(sock+1, &toRead, NULL /* toWrite */, &haveError, NULL ); + /* + * Here numSockets < 0 should be technically checked first for + * proper error handling. However, this function wasn't modified + * since it isn't using in our code. Changing it would be risky + * due to the lack of coverage, and the external extensions might + * still rely on it. + */ if ( errno == EINTR) { return false; diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c index 06fbead100c8..0305c3cf82ba 100644 --- a/src/backend/replication/logical/snapbuild.c +++ b/src/backend/replication/logical/snapbuild.c @@ -1684,6 +1684,7 @@ SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn) /* read statically sized portion of snapshot */ + errno = 0; readBytes = read(fd, &ondisk, SnapBuildOnDiskConstantSize); if (readBytes != SnapBuildOnDiskConstantSize) { diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index 5503ac4e76fe..a8982f7373d6 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -1326,6 +1326,7 @@ RestoreSlotFromDisk(const char *name) END_CRIT_SECTION(); /* read part of statefile that's guaranteed to be version independent */ + errno = 0; readBytes = read(fd, &cp, ReplicationSlotOnDiskConstantSize); if (readBytes != ReplicationSlotOnDiskConstantSize) { diff --git a/src/backend/utils/cache/relmapper.c b/src/backend/utils/cache/relmapper.c index d6e3ca348190..3e5d77c09ec0 100644 --- a/src/backend/utils/cache/relmapper.c +++ b/src/backend/utils/cache/relmapper.c @@ -656,6 +656,7 @@ load_relmap_file(bool shared) * look, the sinval signaling mechanism will make us re-read it before we * are able to access any relation that's affected by the change. */ + errno = 0; if (read(fd, map, sizeof(RelMapFile)) != sizeof(RelMapFile)) ereport(FATAL, (errcode_for_file_access(), diff --git a/src/bin/gpfdist/gpfdist.c b/src/bin/gpfdist/gpfdist.c index af2fcd65ca76..a42f24897657 100644 --- a/src/bin/gpfdist/gpfdist.c +++ b/src/bin/gpfdist/gpfdist.c @@ -4697,6 +4697,7 @@ static int gpfdist_socket_send(const request_t *r, const void *buf, const size_t */ static int gpfdist_SSL_send(const request_t *r, const void *buf, const size_t buflen) { + errno = 0; /* Write the data to socket */ int n = BIO_write(r->io, buf, buflen); diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index bc8ce855d8cd..5a7e6b315047 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2274,7 +2274,7 @@ setup_cdb_schema(void) /* Collect all files with .sql suffix in array. */ nscripts = 0; - while ((file = readdir(dir)) != NULL) + while (errno = 0, (file = readdir(dir)) != NULL) { int namelen = strlen(file->d_name); @@ -2296,9 +2296,7 @@ setup_cdb_schema(void) errno = 0; #endif - closedir(dir); - - if (errno != 0) + if (errno) { /* some kind of I/O error? */ printf(_("error while reading cdb_init.d directory: %s\n"), @@ -2307,6 +2305,8 @@ setup_cdb_schema(void) exit_nicely(); } + closedir(dir); + /* * Sort the array. This allows simple dependencies between scripts, by * naming them like "01_before.sql" and "02_after.sql" diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 6fb8dabb5a40..0460532be906 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -146,6 +146,7 @@ main(int argc, char *argv[]) exit(2); } + errno = 0; if (read(fd, &ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData)) { fprintf(stderr, _("%s: could not read file \"%s\": %s\n"), diff --git a/src/bin/pg_rewind/file_ops.c b/src/bin/pg_rewind/file_ops.c index a8eb04846571..d331d61c8e94 100644 --- a/src/bin/pg_rewind/file_ops.c +++ b/src/bin/pg_rewind/file_ops.c @@ -348,6 +348,7 @@ slurpFile(const char *datadir, const char *path, size_t *filesize) buffer = pg_malloc(len + 1); + errno = 0; if (read(fd, buffer, len) != len) pg_fatal("could not read file \"%s\": %s\n", fullpath, strerror(errno)); diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c index 673f6d3bcea1..490f4e8b8a36 100644 --- a/src/bin/pg_rewind/parsexlog.c +++ b/src/bin/pg_rewind/parsexlog.c @@ -291,6 +291,7 @@ SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, return -1; } + errno = 0; if (read(xlogreadfd, readBuf, XLOG_BLCKSZ) != XLOG_BLCKSZ) { printf(_("could not read from file \"%s\": %s\n"), xlogfpath, diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c index a8c01a8e3889..d1e3261d2b30 100644 --- a/src/interfaces/libpq/fe-lobj.c +++ b/src/interfaces/libpq/fe-lobj.c @@ -785,6 +785,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename) */ while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0) { + errno = 0; tmp = write(fd, buf, nbytes); if (tmp != nbytes) { From 416654b9cfce2665b4af8ae16daa8572b81c189b Mon Sep 17 00:00:00 2001 From: Denis Garsh Date: Mon, 16 Feb 2026 18:55:52 +0300 Subject: [PATCH 070/111] Add instructions on overriding the target directory in README.md (#252) Update the README to describe how to override the default greengage-db-devel directory during installation via the gpAux Makefile by using the GPDIR and GPROOT options. Ticket: GG-194 --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 75cd868bd59b..5ba48b9693a8 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,11 @@ To create optimized release build, use the following: ``` make GPROOT=~/build PARALLEL_MAKE_OPTS=-j8 dist -C gpAux ``` +where `GPROOT` defines the base installation directory. + +By default, the target installation directory is `greengage-db-devel`. +You can override this directory name by setting the `GPDIR` option. +For example, to install into `/usr/local/gpdb`: `make GPROOT=/usr/local GPDIR=gpdb dist -C gpAux`. To run regression tests, a debug build with debug extensions is required. It can be built using the following command: From d094cb27519729e67998206184eb1b7121a3dc01 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Tue, 17 Feb 2026 18:08:08 +0700 Subject: [PATCH 071/111] Fix the out-of-space error for 6.x (#255) SQL Dump workflow: use new composite action `maximize-disk-space` instead of `Move Docker storage to /mnt/docker` step Bump workflows versions: - greengage-reusable-build.yml v13 --> v19 - greengage-reusable-tests-behave.yml v18 --> v19 - greengage-reusable-tests-regression.yml v9 --> v19 - greengage-reusable-tests-orca.yml v12 --> v19 - greengage-reusable-upload.yml v17 --> v19 New in `v19`: - Removed `Move Docker storage to /mnt/docker` step from all workflows - Added a new composite action `mv-docker` for moving a Docker directory for compatibility with older runners with two disks - Added a new composite action `maximize-disk-space`: - Moves Docker storage for backward compatibility across runner generations via new `mv-docker` composite action - Removes unused tools: - Display before/after disks info Main task: GG-197 --- .github/workflows/greengage-ci.yml | 10 +++++----- .github/workflows/greengage-sql-dump.yml | 10 ++-------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index 1878182f923d..ebc80da8ac53 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -23,7 +23,7 @@ jobs: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-build.yml@v13 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-build.yml@v19 with: version: 6 target_os: ${{ matrix.target_os }} @@ -41,7 +41,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v18 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v19 with: version: 6 target_os: ${{ matrix.target_os }} @@ -59,7 +59,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-regression.yml@v9 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-regression.yml@v19 with: version: 6 target_os: ${{ matrix.target_os }} @@ -77,7 +77,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-orca.yml@v12 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-orca.yml@v19 with: version: 6 target_os: ${{ matrix.target_os }} @@ -113,7 +113,7 @@ jobs: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-upload.yml@v17 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-upload.yml@v19 with: version: 6 target_os: ${{ matrix.target_os }} diff --git a/.github/workflows/greengage-sql-dump.yml b/.github/workflows/greengage-sql-dump.yml index 40d1fcab67c4..54b68ab81ea6 100644 --- a/.github/workflows/greengage-sql-dump.yml +++ b/.github/workflows/greengage-sql-dump.yml @@ -38,14 +38,8 @@ jobs: echo "Author: ${{ github.event.workflow_run.head_commit.author.name }}" echo "==================================================================" - - name: Move Docker storage to /mnt/docker - run: | - sudo bash -c ' - systemctl stop docker - mv /var/lib/docker /mnt/docker - echo "{ \"data-root\": \"/mnt/docker\" }" > /etc/docker/daemon.json - systemctl start docker - ' + - name: Maximize disk space + uses: greengagedb/greengage-ci/.github/actions/maximize-disk-space@v19 - name: Determine version from branch id: version From badc6cc4941f366b4cda029c5a029377ce5a4549 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Wed, 18 Feb 2026 15:20:38 +0700 Subject: [PATCH 072/111] Bump greengage-reusable-upload to v21 tag (#259) Bump greengage-reusable-upload to v21 tag Place Login step immediately before pushing to the DockerHub registry Task: ADBDEV-9252 --- .github/workflows/greengage-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index ebc80da8ac53..5a44c3319d7b 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -113,7 +113,7 @@ jobs: contents: read # Explicit for default behavior packages: write # Required for GHCR access actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-upload.yml@v19 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-upload.yml@v21 with: version: 6 target_os: ${{ matrix.target_os }} From 4d66846eedfce01c75ddc71f344f950cbe9f99c8 Mon Sep 17 00:00:00 2001 From: Maxim Michkov Date: Wed, 18 Feb 2026 16:47:07 +0300 Subject: [PATCH 073/111] Make isolation2 tests runnable from any directory (#250) Previously it was necessary to change the current directory to src/test/isolation2 before running pg_isolation2_regress, which caused issues with extensions overwriting diffs from main tests. This was needed because pg_isolation2_regress was looking for sql_isolation_testcase.py in the current directly. Instead we should find sql_isolation_testcase.py in the same directory as pg_isolation2_regress, the same way it is already done by pg_regress and its dependencies gpdiff.pl and gpstringsubs.pl. Also add shebang and execution permission to sql_isolation_testcase.py to use the existing find_other_exec function. Ticket: GG-193 --- gpcontrib/temp_tables_stat/.gitignore | 3 +++ gpcontrib/temp_tables_stat/Makefile | 7 +++--- src/test/isolation2/isolation2_main.c | 22 ++++++++++++++++++- src/test/isolation2/sql_isolation_testcase.py | 2 ++ 4 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 gpcontrib/temp_tables_stat/.gitignore mode change 100644 => 100755 src/test/isolation2/sql_isolation_testcase.py diff --git a/gpcontrib/temp_tables_stat/.gitignore b/gpcontrib/temp_tables_stat/.gitignore new file mode 100644 index 000000000000..e710f0e47270 --- /dev/null +++ b/gpcontrib/temp_tables_stat/.gitignore @@ -0,0 +1,3 @@ +results/ +regression.diffs +regression.out diff --git a/gpcontrib/temp_tables_stat/Makefile b/gpcontrib/temp_tables_stat/Makefile index 2476f77cc880..eb91caa0742e 100644 --- a/gpcontrib/temp_tables_stat/Makefile +++ b/gpcontrib/temp_tables_stat/Makefile @@ -20,10 +20,9 @@ include $(top_srcdir)/contrib/contrib-global.mk endif installcheck: - cd ../../src/test/isolation2/ && \ - make install && \ - ./pg_isolation2_regress \ + $(MAKE) -C $(top_srcdir)/src/test/isolation2 install + $(top_srcdir)/src/test/isolation2/pg_isolation2_regress \ $(EXTRA_REGRESS_OPTS) \ - --inputdir=${CURDIR} \ + --inputdir=$(srcdir) \ --dbname=temp_tables_stat_isolation \ temp_tables_stat diff --git a/src/test/isolation2/isolation2_main.c b/src/test/isolation2/isolation2_main.c index 5c2b9a88f94e..f641fd38478d 100644 --- a/src/test/isolation2/isolation2_main.c +++ b/src/test/isolation2/isolation2_main.c @@ -15,6 +15,8 @@ #include #include +static char sql_isolation_testcase[MAXPGPATH]; + /* * start a Python isolation tester process for specified file (including * redirection), and return process ID @@ -107,7 +109,8 @@ isolation_start_test(const char *testname, "%s ", launcher); snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset, - "python ./sql_isolation_testcase.py --dbname=\"%s\" --initfile_prefix=\"%s\" < \"%s\" > \"%s\" 2>&1", + "python %s --dbname=\"%s\" --initfile_prefix=\"%s\" < \"%s\" > \"%s\" 2>&1", + sql_isolation_testcase, dblist->str, outfile, infile, @@ -130,6 +133,23 @@ isolation_init(int argc, char **argv) { /* set default regression database name */ add_stringlist_item(&dblist, "isolation2test"); + + /* find python script to run */ + if (find_other_exec(argv[0], "sql_isolation_testcase.py", + NULL, sql_isolation_testcase) != 0) + { + char full_path[MAXPGPATH]; + const char *progname = get_progname(argv[0]); + + if (find_my_exec(argv[0], full_path) < 0) + strlcpy(full_path, progname, sizeof(full_path)); + + fprintf(stderr, + _("The program \"sql_isolation_testcase.py\" is needed by %s " + "but was not found in the same directory as \"%s\".\n"), + progname, full_path); + exit(1); + } } int diff --git a/src/test/isolation2/sql_isolation_testcase.py b/src/test/isolation2/sql_isolation_testcase.py old mode 100644 new mode 100755 index 7e32fe6c7a27..95134c1ff0f1 --- a/src/test/isolation2/sql_isolation_testcase.py +++ b/src/test/isolation2/sql_isolation_testcase.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + """ Copyright (c) 2004-Present Pivotal Software, Inc. From f7a1bc898932f7332af017bf9d3f0154ed573175 Mon Sep 17 00:00:00 2001 From: Georgy Shelkovy Date: Thu, 19 Feb 2026 08:56:36 +0500 Subject: [PATCH 074/111] Fix behave tests in the concourse cluster (#207) The behave tests were originally intended to run on two clusters: local demo cluster and also a "concourse cluster" consisting of several machines. The concourse cluster tests were disabled a long time ago when the fork was created, so they weren't running on CI, and so a lot of them went out of date and were broken. 1) Explicitly set the PGPORT environment variable, and also use it. 2) Don't skip gpstate in the before_feature function for concourse cluster, as these tests require creating a cluster in the concourse cluster. 3) In the before_feature function, explicitly disable tests with the concourse_cluster tag unless the concourse_cluster tag is explicitly specified at startup (or they are not tagged with the demo_cluster tag). 4) The gprecoverseg and gprecoverseg_newhost tests in the concourse cluster require a cluster with 2 and 4 segments, respectively, while the other tests require 3 segments. Explicitly mark the two specified features with the tags concourse_cluster_2 and concourse_cluster_4, which can then be used in the init_cluster function to create clusters of the required size on multiple hosts for concourse cluster tests. 5) gprecoverseg_newhost behave test requires sdw4, sdw5 and sdw6 hosts. Increase the number of hosts in the ci/docker-compose.yaml file. 6) There are no systemctl services in the Docker container, use specific command to restart the sshd utility. 7) gpaddmirrors behave test with HBA_HOSTNAMES=1 expects address resolution to a hostname, without prefixes or postfixes. In a container, when resolving an address to a hostname, extra prefixes and postfixes are appended to the hostname by default. To avoid this, explicitly configure hostname resolution using the normal method via /etc/hosts on all hosts. 8) Explicitly set the LANG language in the Dockerfile to fix gpconfig test. 9) Restore the tag loop and replace it with concourse_cluster and ~concourse_cluster to avoid running the same test twice. Now, behave tests run either on the concourse cluster or on the demo, while tests with both tags (concourse and demo) run only on the concourse cluster 10) Remove the grep-based filter for excluding some features, use the @skip tag directly in the features to be disabled. 11) Add pretty output to standard output to see progress when running long tests. 12) In tests that created the cluster themselves, the "data" prefix was used for directories and base ports of 20500 and 21500 were used, while in tests that used the pre-created concourse cluster, directories were used without an additional prefix and the base ports were 20000 and 21000. To reduce current and future diffs, take into account the different cluster settings. Ticket: GG-138 Co-authored-by: Maxim Michkov Co-authored-by: Vasiliy Ivanov --- ci/Dockerfile.ubuntu | 1 + ci/docker-compose.yaml | 21 ++++++++++++++ ci/readme.md | 4 ++- ci/scripts/init_containers.sh | 8 +++++ ci/scripts/run_behave_tests.bash | 10 +++---- gpMgmt/test/README | 8 +++++ gpMgmt/test/behave/mgmt_utils/environment.py | 11 +++++-- .../behave/mgmt_utils/gprecoverseg.feature | 1 + .../mgmt_utils/gprecoverseg_newhost.feature | 1 + .../behave/mgmt_utils/steps/mgmt_utils.py | 29 ++++++++++--------- .../steps/unreachable_hosts_mgmt_utils.py | 2 +- gpMgmt/test/behave_utils/ci/fixtures.py | 28 ++++++++++++++---- gpMgmt/test/behave_utils/cluster_setup.py | 23 ++++++++------- gpMgmt/test/behave_utils/utils.py | 4 +++ 14 files changed, 111 insertions(+), 40 deletions(-) diff --git a/ci/Dockerfile.ubuntu b/ci/Dockerfile.ubuntu index 140787cf79cb..4df067e7127f 100644 --- a/ci/Dockerfile.ubuntu +++ b/ci/Dockerfile.ubuntu @@ -23,6 +23,7 @@ RUN set -eux; \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/cache/man/ +ENV LANG=en_US.UTF-8 ENV CONFIGURE_FLAGS="--enable-debug-extensions --with-gssapi --enable-cassert --enable-debug --enable-depend" FROM base AS build diff --git a/ci/docker-compose.yaml b/ci/docker-compose.yaml index 880da9b623fb..48bd4945bc34 100644 --- a/ci/docker-compose.yaml +++ b/ci/docker-compose.yaml @@ -40,3 +40,24 @@ services: - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa - ${PWD}/logs_sdw3:/logs + sdw4: + <<: *common-service + hostname: sdw4 + volumes: + - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub + - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/logs_sdw4:/logs + sdw5: + <<: *common-service + hostname: sdw5 + volumes: + - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub + - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/logs_sdw5:/logs + sdw6: + <<: *common-service + hostname: sdw6 + volumes: + - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub + - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/logs_sdw6:/logs diff --git a/ci/readme.md b/ci/readme.md index eb31787b71ae..d3c35d5a9a30 100644 --- a/ci/readme.md +++ b/ci/readme.md @@ -89,7 +89,9 @@ It required to add `gpMgmt/tests` directory to `PYTHONPATH`. Greengage cluster in Docker containers has its own peculiarities in preparing a cluster for tests. All tests are run in one way or another on the demo cluster, wherever possible. -For example, cross_subnet tests or tests with tag `concourse_cluster` currently not worked because of too complex cluster preconditions. +For example, cross_subnet tests currently not worked because of too complex cluster preconditions. +Behave tests run either on the concourse cluster (cluster on several hosts) or on the demo (cluster on single host), +while tests with both tags (concourse and demo) run only on the concourse cluster to avoid running the same test twice. Tests in a `docker compose` cluster use the same ssh keys for `gpadmin` user and pre-add the cluster hosts to `.ssh/know_hosts` and `/etc/hosts`. diff --git a/ci/scripts/init_containers.sh b/ci/scripts/init_containers.sh index 3b30e3fd3fd5..a54d2dafc91e 100644 --- a/ci/scripts/init_containers.sh +++ b/ci/scripts/init_containers.sh @@ -30,3 +30,11 @@ do $service bash -c "ssh-keyscan ${services/$service/} >> /home/gpadmin/.ssh/known_hosts" & done wait + +# Add ip and host names of all cluster nodes to /etc/hosts +for service in $services +do + docker compose -p $project -f ci/docker-compose.yaml exec -T \ + $service bash -c "for HOST in $services; do echo \"\$(host \"\$HOST\" | grep 'has address' | head -n 1 | cut -d ' ' -f 4) \$HOST\" >>/etc/hosts; done" & +done +wait diff --git a/ci/scripts/run_behave_tests.bash b/ci/scripts/run_behave_tests.bash index 5bce1599cdb3..6e8dbd32feb8 100755 --- a/ci/scripts/run_behave_tests.bash +++ b/ci/scripts/run_behave_tests.bash @@ -3,10 +3,7 @@ set -x -o pipefail behave_tests_dir="gpMgmt/test/behave/mgmt_utils" -# TODO concourse_cluster tests are not stable -# clusters="concourse_cluster ~concourse_cluster,demo_cluster" - -clusters="~concourse_cluster" +clusters="concourse_cluster ~concourse_cluster" docker_compose_path="ci/docker-compose.yaml" @@ -15,7 +12,7 @@ then # TODO cross_subnet and gpssh tests are excluded # FIXME! sigar is requred for gpperfmon tests # FIXME! /home/gpadmin/sqldump/dump.sql is required for gpexpand tests - features=`ls $behave_tests_dir -1 | grep feature | grep -v -E "cross_subnet|gpssh|gpperfmon|gpexpand" | sed 's/\.feature$//'` + features=`ls $behave_tests_dir -1 | grep feature | sed 's/\.feature$//'` else for feature in $@ do @@ -54,7 +51,8 @@ run_feature() { -f behave_utils.ci.formatter:CustomFormatter \ -o non-existed-output \ -f allure_behave.formatter:AllureFormatter \ - -o /tmp/allure-results" \ + -o /tmp/allure-results \ + -f pretty" \ cdw gpdb_src/ci/scripts/behave_gpdb.bash status=$? diff --git a/gpMgmt/test/README b/gpMgmt/test/README index bf7111ef3a4d..3672c9e3106b 100644 --- a/gpMgmt/test/README +++ b/gpMgmt/test/README @@ -22,6 +22,14 @@ integration tests. # this will run all tests tagged with smoke make -f Makefile.behave behave tags=smoke +# this will run all tests not tagged with concourse_cluster +# (on demo cluster, i.e. cluster on single host) +make -f Makefile.behave behave flags="--tags ~concourse_cluster" + +# this will run all tests tagged with concourse_cluster +# (on concourse cluster, i.e. cluster on several hosts) +make -f Makefile.behave behave flags="--tags concourse_cluster" + # this will run all tests tagged with smoke AND mirrors make -f Makefile.behave behave flags="--tags smoke --tags mirrors" diff --git a/gpMgmt/test/behave/mgmt_utils/environment.py b/gpMgmt/test/behave/mgmt_utils/environment.py index 244b707d6af1..8cc64a8a4223 100644 --- a/gpMgmt/test/behave/mgmt_utils/environment.py +++ b/gpMgmt/test/behave/mgmt_utils/environment.py @@ -5,7 +5,7 @@ from behave import use_fixture from test.behave_utils.utils import drop_database_if_exists, start_database_if_not_started,\ - create_database, \ + create_database, is_concourse_cluster, \ run_command, check_user_permissions, run_gpcommand, execute_sql from steps.mirrors_mgmt_utils import MirrorMgmtContext from steps.gpconfig_mgmt_utils import GpConfigContext @@ -20,8 +20,10 @@ def before_all(context): def before_feature(context, feature): # we should be able to run gpexpand without having a cluster initialized - tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpstate', + tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpssh-exkeys', 'gpinitsystem', 'cross_subnet'] + if not is_concourse_cluster(context): + tags_to_skip.append('gpstate') if set(context.feature.tags).intersection(tags_to_skip): return @@ -106,6 +108,11 @@ def before_scenario(context, scenario): scenario.skip("skipping scenario tagged with @skip") return + if "concourse_cluster" in scenario.effective_tags and \ + "demo_cluster" not in scenario.effective_tags and \ + not is_concourse_cluster(context): + raise Exception("This test can only be run under concourse cluster.") + if 'gpmovemirrors' in context.feature.tags: context.mirror_context = MirrorMgmtContext() diff --git a/gpMgmt/test/behave/mgmt_utils/gprecoverseg.feature b/gpMgmt/test/behave/mgmt_utils/gprecoverseg.feature index 9b6379ae26c2..1a49b6f8bcb0 100644 --- a/gpMgmt/test/behave/mgmt_utils/gprecoverseg.feature +++ b/gpMgmt/test/behave/mgmt_utils/gprecoverseg.feature @@ -1,4 +1,5 @@ @gprecoverseg +@concourse_cluster_2 Feature: gprecoverseg tests @demo_cluster diff --git a/gpMgmt/test/behave/mgmt_utils/gprecoverseg_newhost.feature b/gpMgmt/test/behave/mgmt_utils/gprecoverseg_newhost.feature index 9d7e9bff306a..383fdc48e67c 100644 --- a/gpMgmt/test/behave/mgmt_utils/gprecoverseg_newhost.feature +++ b/gpMgmt/test/behave/mgmt_utils/gprecoverseg_newhost.feature @@ -1,4 +1,5 @@ @gprecoverseg_newhost +@concourse_cluster_4 Feature: gprecoverseg tests involving migrating to a new host ########################### @concourse_cluster tests ########################### diff --git a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py index 7c3542ddda3b..2f995b7745ba 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py @@ -3119,16 +3119,17 @@ def _create_working_directory(context, working_directory, mode=''): os.mkdir(context.working_directory) -def _create_cluster(context, master_host, segment_host_list, hba_hostnames='0', with_mirrors=False, mirroring_configuration='group'): +def _create_cluster(context, master_host, segment_host_list, hba_hostnames='0', with_mirrors=False, mirroring_configuration='group', datadir_prefix='data', port_base='20500', mirror_port_base='21500'): if segment_host_list == "": segment_host_list = [] else: segment_host_list = segment_host_list.split(",") global master_data_dir - master_data_dir = os.path.join(context.working_directory, 'data/master/gpseg-1') + master_data_dir = os.path.join(context.working_directory, datadir_prefix, 'master', 'gpseg-1') os.environ['MASTER_DATA_DIRECTORY'] = master_data_dir os.environ['PGPORT'] = '10300' + context.datadir_prefix = datadir_prefix try: with dbconn.connect(dbconn.DbURL(dbname='template1'), unsetSearchPath=False) as conn: @@ -3143,7 +3144,7 @@ def _create_cluster(context, master_host, segment_host_list, hba_hostnames='0', except: pass - testcluster = TestCluster(hosts=[master_host]+segment_host_list, base_dir=context.working_directory,hba_hostnames=hba_hostnames) + testcluster = TestCluster(hosts=[master_host]+segment_host_list, base_dir=context.working_directory, hba_hostnames=hba_hostnames, datadir_prefix=datadir_prefix, port_base=port_base, mirror_port_base=mirror_port_base) testcluster.reset_cluster() testcluster.create_cluster(with_mirrors=with_mirrors, mirroring_configuration=mirroring_configuration) context.gpexpand_mirrors_enabled = with_mirrors @@ -3164,6 +3165,10 @@ def impl(context, master_host, segment_host_list, hba_hostnames): def impl(context, master_host, segment_host_list): _create_cluster(context, master_host, segment_host_list, with_mirrors=True, mirroring_configuration='group') +@given('a cluster is created with mirrors on "{master_host}" and "{segment_host_list}" from fixture') +def impl(context, master_host, segment_host_list): + _create_cluster(context, master_host, segment_host_list, with_mirrors=True, mirroring_configuration='group', datadir_prefix='', port_base='20000', mirror_port_base='21000') + @given('a cluster is created with "{mirroring_configuration}" segment mirroring on "{master_host}" and "{segment_host_list}"') def impl(context, mirroring_configuration, master_host, segment_host_list): _create_cluster(context, master_host, segment_host_list, with_mirrors=True, mirroring_configuration=mirroring_configuration) @@ -3308,15 +3313,11 @@ def impl(context): @given('the master pid has been saved') def impl(context): - data_dir = os.path.join(context.working_directory, - 'data/master/gpseg-1') - context.master_pid = gp.get_postmaster_pid_locally(data_dir) + context.master_pid = gp.get_postmaster_pid_locally(master_data_dir) @then('verify that the master pid has not been changed') def impl(context): - data_dir = os.path.join(context.working_directory, - 'data/master/gpseg-1') - current_master_pid = gp.get_postmaster_pid_locally(data_dir) + current_master_pid = gp.get_postmaster_pid_locally(master_data_dir) if context.master_pid == current_master_pid: return @@ -3534,9 +3535,9 @@ def make_temp_dir(context, tmp_base_dir, mode=''): def impl(context, hostnames): hosts = hostnames.split(',') if hasattr(context, "working_directory"): - reset_hosts(hosts, context.working_directory) + reset_hosts(hosts, context.working_directory, context.datadir_prefix) if hasattr(context, "temp_base_dir"): - reset_hosts(hosts, context.temp_base_dir) + reset_hosts(hosts, context.temp_base_dir, context.datadir_prefix) @given('user has created expansiontest tables') @@ -4424,13 +4425,13 @@ def impl(context): # Insert data into table and run checkpoint just before syncing pg_control if [[ "\$arguments" == *"pg_xlog"* ]] then - ssh cdw "source /usr/local/greengage-db-devel/greengage_path.sh; psql -c 'INSERT INTO test_recoverseg SELECT generate_series(1, 1000)' -d postgres -p 5432 -h cdw" + ssh cdw "source /usr/local/greengage-db-devel/greengage_path.sh; psql -c 'INSERT INTO test_recoverseg SELECT generate_series(1, 1000)' -d postgres -p {port} -h cdw" # run checkpoint - ssh cdw "source /usr/local/greengage-db-devel/greengage_path.sh; psql -c "CHECKPOINT" -d postgres -p 5432 -h cdw" + ssh cdw "source /usr/local/greengage-db-devel/greengage_path.sh; psql -c 'CHECKPOINT' -d postgres -p {port} -h cdw" fi /usr/bin/rsync \$arguments EOL -""" +""".format(port=os.environ.get("PGPORT")) clear_cmd_cache_script = """ cat >/tmp/clear_cmd_cache.py < 0: + context.execute_steps(u""" + Given the database is not running + And a working directory of the test as '/data/gpdata' + And the user runs command "rm -rf ~/gpAdminLogs/gpinitsystem*" + And a cluster is created with mirrors on "cdw" and "{}" from fixture + """.format(','.join('sdw{}'.format(i + 1) for i in range(segment_hosts_in_cluster)))) + else: + context.execute_steps(u""" + Given the database is not running + And the user runs command "rm -rf ~/gpAdminLogs/gpinitsystem*" + And a standard local demo cluster is created + """) diff --git a/gpMgmt/test/behave_utils/cluster_setup.py b/gpMgmt/test/behave_utils/cluster_setup.py index 61644d15cdc2..6a36319f6df9 100755 --- a/gpMgmt/test/behave_utils/cluster_setup.py +++ b/gpMgmt/test/behave_utils/cluster_setup.py @@ -20,7 +20,7 @@ def run(self, validate=True): class TestCluster: - def __init__(self, hosts = None, base_dir = '/tmp/default_gpinitsystem', hba_hostnames='0'): + def __init__(self, hosts = None, base_dir = '/tmp/default_gpinitsystem', hba_hostnames='0', datadir_prefix='data', port_base='20500', mirror_port_base='21500'): """ hosts: lists of cluster hosts. master host will be assumed to be the first element. base_dir: cluster directory @@ -29,13 +29,14 @@ def __init__(self, hosts = None, base_dir = '/tmp/default_gpinitsystem', hba_hos master_host = 'localhost' segments_host = socket.gethostname() self.hosts = [master_host, segments_host] + self.datadir_prefix = datadir_prefix if hosts: self.hosts = hosts - self.port_base = '20500' + self.port_base = port_base self.master_port = os.environ.get('PGPORT', '10300') - self.mirror_port_base = '21500' + self.mirror_port_base = mirror_port_base self.gpinitconfig_template = local_path('configs/gpinitconfig_template') self.gpinitconfig_mirror_template = local_path('configs/gpinitconfig_mirror_template') @@ -47,9 +48,9 @@ def __init__(self, hosts = None, base_dir = '/tmp/default_gpinitsystem', hba_hos self.hosts_file = os.path.join(self.base_dir, 'hosts') self.gpexpand_file = os.path.join(self.base_dir, 'gpexpand_input') - self.primary_dir = os.path.join(self.base_dir, 'data/primary') - self.mirror_dir = os.path.join(self.base_dir, 'data/mirror') - self.master_dir = os.path.join(self.base_dir, 'data/master') + self.primary_dir = os.path.join(self.base_dir, self.datadir_prefix, 'primary') + self.mirror_dir = os.path.join(self.base_dir, self.datadir_prefix, 'mirror') + self.master_dir = os.path.join(self.base_dir, self.datadir_prefix, 'master') # Test metadata # Whether to do gpinitsystem or not @@ -93,7 +94,7 @@ def _generate_gpinit_config_files(self): substitute_strings_in_file(config_template, self.init_file, transforms) def reset_cluster(self): - reset_hosts(self.hosts, test_base_dir = self.base_dir) + reset_hosts(self.hosts, self.base_dir, self.datadir_prefix) def create_cluster(self, with_mirrors=False, mirroring_configuration='group'): # Generate the config files to initialize the cluster @@ -166,11 +167,11 @@ def run_shell_command(cmdstr, cmdname = 'shell command', results={'rc':0, 'stdou print "command error: %s" % results['stderr'] return results -def reset_hosts(hosts, test_base_dir): +def reset_hosts(hosts, test_base_dir, datadir_prefix): - primary_dir = os.path.join(test_base_dir, 'data', 'primary') - mirror_dir = os.path.join(test_base_dir, 'data', 'mirror') - master_dir = os.path.join(test_base_dir, 'data', 'master') + primary_dir = os.path.join(test_base_dir, datadir_prefix, 'primary') + mirror_dir = os.path.join(test_base_dir, datadir_prefix, 'mirror') + master_dir = os.path.join(test_base_dir, datadir_prefix, 'master') host_args = " ".join(map(lambda x: "-h %s" % x, hosts)) reset_primary_dirs_cmd = "gpssh %s -e 'rm -rf %s; mkdir -p %s'" % (host_args, primary_dir, primary_dir) diff --git a/gpMgmt/test/behave_utils/utils.py b/gpMgmt/test/behave_utils/utils.py index 6af47a0151bf..030d6b6d1b5a 100644 --- a/gpMgmt/test/behave_utils/utils.py +++ b/gpMgmt/test/behave_utils/utils.py @@ -877,3 +877,7 @@ def wait_for_database_dropped(dbname, remaining_attempt = 3000): if remaining_attempt == 0: raise Exception('Unable to drop the database %s !!!') % dbname time.sleep(0.1) + + +def is_concourse_cluster(context): + return context.config.tag_expression.check(context.feature.tags + ["concourse_cluster"]) From ecdda443e482b45aea5c3c1e71af963270a2ea6a Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Thu, 19 Feb 2026 19:31:28 +0700 Subject: [PATCH 075/111] Directory `sqldump` absent for 6.x (#272) Create directory immediately before create dump with `777` permissions Set `777` permissions on `sqldump` directory if exists No need to pack SQL dump in CI Task: ADBDEV-9275 --- concourse/scripts/dumpdb.bash | 3 ++- concourse/scripts/ic_gpdb.bash | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/concourse/scripts/dumpdb.bash b/concourse/scripts/dumpdb.bash index 410e3f32bf7e..40f0c6536464 100755 --- a/concourse/scripts/dumpdb.bash +++ b/concourse/scripts/dumpdb.bash @@ -26,5 +26,6 @@ psql \ echo "" done +mkdir -pm 777 sqldump pg_dumpall -f ./sqldump/dump.sql -xz -z ./sqldump/dump.sql +[ -z "${CI:-}" ] && xz -z ./sqldump/dump.sql diff --git a/concourse/scripts/ic_gpdb.bash b/concourse/scripts/ic_gpdb.bash index 30603f1c76e5..ff7f2d8cffd5 100755 --- a/concourse/scripts/ic_gpdb.bash +++ b/concourse/scripts/ic_gpdb.bash @@ -85,7 +85,7 @@ function _main() { fi if [ "${DUMP_DB}" == "true" ]; then - chmod 777 sqldump + [ -d sqldump ] && chmod 777 sqldump || true su gpadmin -c ./gpdb_src/concourse/scripts/dumpdb.bash fi } From f9c7aaacd3a271a4872a9e9170831a935d8aed6c Mon Sep 17 00:00:00 2001 From: Denis Garsh Date: Fri, 20 Feb 2026 00:31:34 +0300 Subject: [PATCH 076/111] Actualize READMEs for CentOS/RHEL/Rocky (#254) Fix CentOS 7 instruction: add missing packages required for successful build, remove conan installation as it is not used, remove pip installation of Python dependencies since they are only needed for behave tests and are installed separately during Docker image build similar to Ubuntu. Fix RHEL instruction: add a new dependency installation script for RHEL and Rocky and related instructions. Add Rocky support in set_bld_arch.sh to enable builds via gpAux. zstd is now built from source because Rocky does not provide zstd static package and zstd-devel does not include the static library. Remove all extra steps in CentOS and RHEL installation that are not required to build, create a cluster, and run regression tests. Ticket: GG-81 --- README.CentOS.bash | 16 +++++++--- README.Rhel-Rocky.bash | 45 ++++++++++++++++++++++++++ README.linux.md | 62 ++++++++++-------------------------- gpAux/releng/set_bld_arch.sh | 2 +- 4 files changed, 74 insertions(+), 51 deletions(-) create mode 100755 README.Rhel-Rocky.bash diff --git a/README.CentOS.bash b/README.CentOS.bash index cd0aacabc261..bee249e73f57 100755 --- a/README.CentOS.bash +++ b/README.CentOS.bash @@ -14,17 +14,23 @@ sudo yum install -y \ libcurl-devel \ libevent-devel \ libkadm5 \ - libyaml-devel \ + libtool \ + libuuid-devel \ + libuv-devel \ libxml2-devel \ + libxslt-devel \ + libyaml-devel \ libzstd-devel \ + libzstd-static \ + net-tools \ + openldap-devel \ + openssl \ openssl-devel \ + pam-devel \ + perl-Env \ perl-ExtUtils-Embed \ python-devel \ python-pip \ readline-devel \ xerces-c-devel \ zlib-devel - -sudo pip install conan -sudo pip install -r python-dependencies.txt -sudo pip install -r python-developer-dependencies.txt diff --git a/README.Rhel-Rocky.bash b/README.Rhel-Rocky.bash new file mode 100755 index 000000000000..866c646e08fb --- /dev/null +++ b/README.Rhel-Rocky.bash @@ -0,0 +1,45 @@ +#!/bin/bash + +sudo dnf -y update +sudo dnf -y install epel-release +sudo dnf -y install 'dnf-command(config-manager)' +sudo dnf config-manager --set-enabled devel +sudo dnf makecache + +sudo dnf -y install\ + apr-devel \ + bison \ + bzip2-devel \ + cmake3 \ + flex \ + gcc \ + gcc-c++ \ + iproute \ + krb5-devel \ + libcurl-devel \ + libevent-devel \ + libicu \ + libkadm5 \ + libtool \ + libuuid-devel \ + libuv-devel \ + libxml2-devel \ + libxslt-devel \ + libyaml-devel \ + net-tools \ + openldap-devel \ + openssl \ + openssl-devel \ + pam-devel \ + perl-Env \ + perl-ExtUtils-Embed \ + perl-IPC-Run \ + perl-JSON \ + perl-Test-Base \ + procps-ng \ + python2-devel \ + python2-pip \ + readline-devel \ + snappy-devel \ + xerces-c-devel \ + zlib-devel diff --git a/README.linux.md b/README.linux.md index 8a114a34a725..806589bb4ff7 100644 --- a/README.linux.md +++ b/README.linux.md @@ -1,61 +1,33 @@ -## For CentOS: - -- Install Dependencies +## For CentOS 7: +- Install dependencies using README.CentOS.bash script: ```bash ./README.CentOS.bash ``` + Note: CentOS 7 is EOL — configure `yum` to use a valid repo (e.g., `vault.centos.org`) before installing dependencies. -- If you want to link cmake3 to cmake, run: +## For RHEL/Rocky 8: +- Install dependencies using README.Rhel-Rocky.bash script: ```bash - sudo ln -sf /usr/bin/cmake3 /usr/local/bin/cmake + ./README.Rhel-Rocky.bash ``` -- Make sure that you add `/usr/local/lib` and `/usr/local/lib64` to -`/etc/ld.so.conf`, then run command `ldconfig`. - -- If you want to install and use gcc-6 by default, run: - +- Build and install zstd with static library, e.g.: ```bash - sudo yum install -y centos-release-scl - sudo yum install -y devtoolset-6-toolchain - echo 'source scl_source enable devtoolset-6' >> ~/.bashrc + cd /tmp + curl -LO https://github.com/facebook/zstd/releases/download/v1.4.4/zstd-1.4.4.tar.gz + tar -xf zstd-1.4.4.tar.gz + cd zstd-1.4.4 + make -j$(nproc) + sudo make install PREFIX=/usr/local ``` -## For RHEL: - -- Install Development Tools. - - For RHEL 8: Install `Development Tools`: - - ```bash - sudo yum group install -y "Development Tools" - ``` - - - For RHEL versions (< 8.0): Install `devtoolset-7`: - - ```bash - sudo yum-config-manager --enable rhui-REGION-rhel-server-rhscl - sudo yum install -y devtoolset-7-toolchain - ``` - -- Install dependencies using README.CentOS.bash script. - - For RHEL 8: Execute additional steps before running README.CentOS.bash script. - - Note: Make sure installation of `Development Tools` includes `git` and `make` else install these tools manually. - - ```bash - sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm - sed -i -e 's/python-devel /python2-devel /' -e 's/python-pip/python2-pip/' -e 's/sudo pip/sudo pip2/' README.CentOS.bash - sed -i '/xerces-c-devel/d' README.CentOS.bash - sudo ln -s /usr/bin/python2.7 /usr/bin/python - ``` - - - Install dependencies using README.CentOS.bash script. +- Create symbolic link to Python 2 in `/usr/bin`: - ```bash - ./README.CentOS.bash - ``` + ```bash + sudo ln -s python2 /usr/bin/python + ``` ## For Ubuntu (22.04): diff --git a/gpAux/releng/set_bld_arch.sh b/gpAux/releng/set_bld_arch.sh index efe799020d8d..3fecae9a27bc 100755 --- a/gpAux/releng/set_bld_arch.sh +++ b/gpAux/releng/set_bld_arch.sh @@ -7,7 +7,7 @@ case "`uname -s`" in if [ -f /etc/redhat-release -a ! -f /etc/altlinux-release -a ! -f /etc/redos-release ]; then case "`cat /etc/redhat-release`" in *) - BLD_ARCH_HOST="rhel`cat /etc/redhat-release | sed -e 's/CentOS Linux/RedHat/' -e 's/Red Hat Enterprise Linux/RedHat/' -e 's/WS//' -e 's/Server//' -e 's/Client//' | awk '{print $3}' | awk -F. '{print $1}'`_`uname -m | sed -e s/i686/x86_32/`" + BLD_ARCH_HOST="rhel`cat /etc/redhat-release | sed -e 's/CentOS Linux/RedHat/' -e 's/Red Hat Enterprise Linux/RedHat/' -e 's/Rocky Linux/RedHat/' -e 's/WS//' -e 's/Server//' -e 's/Client//' | awk '{print $3}' | awk -F. '{print $1}'`_`uname -m | sed -e s/i686/x86_32/`" ;; esac fi From b60f0f097fd7fbee890b4089230bbda038d6a22c Mon Sep 17 00:00:00 2001 From: Ivan Sergeenko Date: Fri, 20 Feb 2026 14:07:10 +0300 Subject: [PATCH 077/111] Prevent ORCA from losing distribution requests for 6.x (#257) In the case when the join condition contains AND clauses, hash join performs redistribution request by every subexpression in the condition. The problem occurred when several of these subexpressions had the same right-hand side, making the matching logic inside CPhysicalHashJoin::PdshashedMatching select the same subexpression for every request, therefore losing possible redistributions. This patch adds a parameter to the function that specifies the index of the initial distribution request index. Subexpressions matching this index are now prioritized in the matching logic. Also, as a side-effect of the research went into the patch, several explanatory comments are added to make the code easier to understand. Ticket: GG-207 (cherry picked from commit ada9b90) Changes from original commit: - Test results were adjusted to account for differences between branches --- ...JoinUsingOperatorsOfNonDefaultOpfamily.mdp | 2 +- .../data/dxl/minidump/CTE15HAReplicated.mdp | 2 +- .../data/dxl/minidump/CTE15Replicated.mdp | 2 +- .../data/dxl/minidump/CTE25Replicated.mdp | 2 +- .../data/dxl/minidump/CTE2HAReplicated.mdp | 2 +- .../data/dxl/minidump/CTE2Replicated.mdp | 2 +- ...ArityAssociativityCommutativityAtLimit.mdp | 2 +- .../data/dxl/minidump/PartTbl-SQScalar.mdp | 2 +- .../dxl/minidump/TPCH-Partitioned-256GB.mdp | 2 +- .../include/gpopt/base/CReqdPropPlan.h | 2 + .../gpopt/operators/CPhysicalHashJoin.h | 5 +- .../src/operators/CPhysicalHashJoin.cpp | 112 ++++++++++++++++-- .../src/operators/CPhysicalInnerHashJoin.cpp | 7 +- .../regress/expected/qp_correlated_query.out | 41 +++++++ .../qp_correlated_query_optimizer.out | 79 ++++++++---- .../regress/expected/update_gp_optimizer.out | 74 ++++++------ src/test/regress/sql/qp_correlated_query.sql | 26 ++++ 17 files changed, 286 insertions(+), 78 deletions(-) diff --git a/src/backend/gporca/data/dxl/minidump/3WayJoinUsingOperatorsOfNonDefaultOpfamily.mdp b/src/backend/gporca/data/dxl/minidump/3WayJoinUsingOperatorsOfNonDefaultOpfamily.mdp index 2abac4deb839..9d0177067ed1 100644 --- a/src/backend/gporca/data/dxl/minidump/3WayJoinUsingOperatorsOfNonDefaultOpfamily.mdp +++ b/src/backend/gporca/data/dxl/minidump/3WayJoinUsingOperatorsOfNonDefaultOpfamily.mdp @@ -396,7 +396,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp index 8a93dc702f64..5ed0472b73a2 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp @@ -416,7 +416,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp index fe68d040693a..c4769642361a 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp @@ -429,7 +429,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE25Replicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE25Replicated.mdp index 3bf623cd9df1..47459a772912 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE25Replicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE25Replicated.mdp @@ -406,7 +406,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE2HAReplicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE2HAReplicated.mdp index a063152e4577..e3adf6d4c862 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE2HAReplicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE2HAReplicated.mdp @@ -385,7 +385,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTE2Replicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE2Replicated.mdp index 4a4166cd7359..69b92b0fc41c 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE2Replicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE2Replicated.mdp @@ -398,7 +398,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAtLimit.mdp b/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAtLimit.mdp index 18f635025bc7..84172c12264c 100644 --- a/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAtLimit.mdp +++ b/src/backend/gporca/data/dxl/minidump/JoinArityAssociativityCommutativityAtLimit.mdp @@ -336,7 +336,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartTbl-SQScalar.mdp b/src/backend/gporca/data/dxl/minidump/PartTbl-SQScalar.mdp index e7efd1128416..fb221318e064 100644 --- a/src/backend/gporca/data/dxl/minidump/PartTbl-SQScalar.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartTbl-SQScalar.mdp @@ -319,7 +319,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/TPCH-Partitioned-256GB.mdp b/src/backend/gporca/data/dxl/minidump/TPCH-Partitioned-256GB.mdp index a348cc4bae26..0679fff9ff46 100644 --- a/src/backend/gporca/data/dxl/minidump/TPCH-Partitioned-256GB.mdp +++ b/src/backend/gporca/data/dxl/minidump/TPCH-Partitioned-256GB.mdp @@ -3673,7 +3673,7 @@ - + diff --git a/src/backend/gporca/libgpopt/include/gpopt/base/CReqdPropPlan.h b/src/backend/gporca/libgpopt/include/gpopt/base/CReqdPropPlan.h index 66f67be0d2e3..7d0031802cd1 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/base/CReqdPropPlan.h +++ b/src/backend/gporca/libgpopt/include/gpopt/base/CReqdPropPlan.h @@ -16,6 +16,8 @@ #include "gpopt/base/CColRef.h" #include "gpopt/base/CReqdProp.h" +#define GPOPT_INVALID_OPT_REQUEST gpos::ulong_max + namespace gpopt { using namespace gpos; diff --git a/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalHashJoin.h b/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalHashJoin.h index 22a1ec43d8af..54a2a5fcc172 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalHashJoin.h +++ b/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalHashJoin.h @@ -56,7 +56,7 @@ class CPhysicalHashJoin : public CPhysicalJoin // compute a distribution matching the distribution delivered by given child CDistributionSpec *PdsMatch(CMemoryPool *mp, CDistributionSpec *pds, - ULONG ulSourceChildIndex) const; + ULONG ulSourceChildIndex, ULONG ulOptReq) const; protected: // compute required hashed distribution from the n-th child @@ -96,7 +96,7 @@ class CPhysicalHashJoin : public CPhysicalJoin // helper for computing a hashed distribution matching the given distribution CDistributionSpecHashed *PdshashedMatching( CMemoryPool *mp, CDistributionSpecHashed *pdshashed, - ULONG ulSourceChild) const; + ULONG ulSourceChild, ULONG ulOptReq) const; // create (singleton, singleton) optimization request CDistributionSpec *PdsRequiredSingleton(CMemoryPool *mp, @@ -220,7 +220,6 @@ class CPhysicalHashJoin : public CPhysicalJoin return dynamic_cast(pop); } - }; // class CPhysicalHashJoin } // namespace gpopt diff --git a/src/backend/gporca/libgpopt/src/operators/CPhysicalHashJoin.cpp b/src/backend/gporca/libgpopt/src/operators/CPhysicalHashJoin.cpp index eeeefe147acf..fc7850fdae12 100644 --- a/src/backend/gporca/libgpopt/src/operators/CPhysicalHashJoin.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CPhysicalHashJoin.cpp @@ -244,7 +244,7 @@ CPhysicalHashJoin::PrsRequired(CMemoryPool *mp, CExpressionHandle &exprhdl, //--------------------------------------------------------------------------- CDistributionSpec * CPhysicalHashJoin::PdsMatch(CMemoryPool *mp, CDistributionSpec *pds, - ULONG ulSourceChildIndex) const + ULONG ulSourceChildIndex, ULONG ulOptReq) const { GPOS_ASSERT(NULL != pds); @@ -267,7 +267,7 @@ CPhysicalHashJoin::PdsMatch(CMemoryPool *mp, CDistributionSpec *pds, // require second child to provide a matching hashed distribution return PdshashedMatching(mp, CDistributionSpecHashed::PdsConvert(pds), - ulSourceChildIndex); + ulSourceChildIndex, ulOptReq); default: GPOS_ASSERT(CDistributionSpec::EdtStrictReplicated == pds->Edt() || @@ -307,9 +307,11 @@ CPhysicalHashJoin::PdsMatch(CMemoryPool *mp, CDistributionSpec *pds, CDistributionSpecHashed * CPhysicalHashJoin::PdshashedMatching( CMemoryPool *mp, CDistributionSpecHashed *pdshashed, - ULONG - ulSourceChild // index of child that delivered the given hashed distribution -) const + // index of child that delivered the given hashed distribution + ULONG ulSourceChild, + // index of the request that created the distribution + // if this value is unknown, GPOPT_INVALID_OPT_REQUEST should be provided instead + ULONG ulOptReq) const { GPOS_ASSERT(2 > ulSourceChild); @@ -340,9 +342,89 @@ CPhysicalHashJoin::PdshashedMatching( CExpression *pexprDlvrd = (*pdrgpexprDist)[ulDlvrdIdx]; CExpressionArray *equiv_distribution_exprs = NULL; if (NULL != all_equiv_exprs && all_equiv_exprs->Size() > 0) + { equiv_distribution_exprs = (*all_equiv_exprs)[ulDlvrdIdx]; + } + + // first, try to match the initial source expression from which this request was derived, when + // 1) we know an optimization request index + // 2) requested distribution is on a single key + // + // if we don't perform this check right away, we might lose a distribution. + // consider the following query, before this logic was introduced: + // + // create table t1 as (select gen::numeric as a from generate_series(1, 20) as gen) distributed by (a); + // create table t2 as (select gen::numeric as a from generate_series(1, 20) as gen) distributed by (a); + // explain (verbose) select t1.a from t1 where t1.a = (select sum(t2.a) from t2 where t1.a = t2.a); + // Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..862.00 rows=1 width=8) + // Output: t1.a + // -> Hash Join (cost=0.00..862.00 rows=1 width=8) + // Output: t1.a + // Hash Cond: (((sum(t2.a)) = t1.a) AND (t2.a = t1.a)) + // -> Redistribute Motion 3:3 (slice2; segments: 3) (cost=0.00..431.00 rows=1 width=16) + // Output: t2.a, (sum(t2.a)) + // Hash Key: (sum(t2.a)) + // -> GroupAggregate (cost=0.00..431.00 rows=1 width=16) + // Output: t2.a, sum(t2.a) + // Group Key: t2.a + // -> Sort (cost=0.00..431.00 rows=1 width=8) + // Output: t2.a + // Sort Key: t2.a + // -> Seq Scan on public.t2 (cost=0.00..431.00 rows=1 width=8) + // Output: t2.a + // -> Hash (cost=431.00..431.00 rows=1 width=8) + // Output: t1.a + // -> Seq Scan on public.t1 (cost=0.00..431.00 rows=1 width=8) + // Output: t1.a + // + // here, the join condition has two expressions, and all of them have the same + // right-hand side. It means that for every single-key redistribution request we get the same + // pexprDlvrd value (t1.a) + // and, when we go through the source expressions in order, + // the first expression will always be selected as matching for every redistribution request, + // meaning that the second expression has no chance to be matched, and therefore the distribution + // by t2.a is not considered + // + // also, it isn't obvious if the same problem might arise when matching a distribution containing + // multiple keys. theoretically, it can, so it might be worth exploring + + if (GPOPT_INVALID_OPT_REQUEST != ulOptReq && ulSourceSize > ulOptReq) + { + CExpression *source_expr = (*pdrgpexprSource)[ulOptReq]; + BOOL fSuccess = CUtils::Equals(pexprDlvrd, source_expr); + if (!fSuccess) + { + // if failed to find a equal match in the source distribution expr + // array, check the equivalent exprs to find a match + fSuccess = + CUtils::Contains(equiv_distribution_exprs, source_expr); + } + if (fSuccess) + { + CExpression *pexprTarget = (*pdrgpexprTarget)[ulOptReq]; + pexprTarget->AddRef(); + pdrgpexpr->Append(pexprTarget); + + if (nullptr != opfamilies) + { + GPOS_ASSERT(nullptr != m_hash_opfamilies); + IMDId *opfamily = (*m_hash_opfamilies)[ulOptReq]; + opfamily->AddRef(); + opfamilies->Append(opfamily); + } + continue; + } + } + + // then, try the rest of the expressions for (ULONG idx = 0; idx < ulSourceSize; idx++) { + // check if we've already mathed this child above + if (idx == ulOptReq) + { + continue; + } + BOOL fSuccess = false; CExpression *source_expr = (*pdrgpexprSource)[idx]; fSuccess = CUtils::Equals(pexprDlvrd, source_expr); @@ -396,7 +478,7 @@ CPhysicalHashJoin::PdshashedMatching( CRefCount::SafeRelease(opfamilies); // try again using the equivalent hashed distribution return PdshashedMatching(mp, pdshashed->PdshashedEquiv(), - ulSourceChild); + ulSourceChild, ulOptReq); } // it should never happen, but instead of creating wrong spec, raise an exception GPOS_RAISE( @@ -638,6 +720,18 @@ CPhysicalHashJoin::PdsRequiredRedistribute(CMemoryPool *mp, } // find the distribution delivered by first child + // it should be noted that it is not guaranteed that the first child derived CDistributionSpecHashed + // for example, we might get a CDistributionSpecSingleton from it, so even though + // the initial request says (redistribute, redistribute), we would get (singleton, singleton) + // distribution as a result of this function + // + // it means that we don't guarantee that any requested distribution can be actually enforced. + // moreover, even when the first child derives CDistributionSpecHashed, the way we match distribution + // expressions inside PdshashedMatching is not perfect, and might possibly distort distribution + // of the second child + // + // this logic is far from being straightforward, but it is not a trivial problem to fix it + // at least you are warned about it CDistributionSpec *pdsFirst = CDrvdPropPlan::Pdpplan((*pdrgpdpCtxt)[0])->Pds(); GPOS_ASSERT(NULL != pdsFirst); @@ -667,7 +761,8 @@ CPhysicalHashJoin::PdsRequiredRedistribute(CMemoryPool *mp, } // return a matching distribution request for the second child - CDistributionSpec *pdsMatch = PdsMatch(mp, pdsInputForMatch, ulFirstChild); + CDistributionSpec *pdsMatch = + PdsMatch(mp, pdsInputForMatch, ulFirstChild, ulOptReq); if (pdsFirst->Edt() == CDistributionSpec::EdtHashed) { // if the input spec was created as a copy, release it @@ -917,7 +1012,8 @@ CPhysicalHashJoin::CreateOptRequests(CMemoryPool *mp) // to be distributed on single hash join keys separately, as well as the set // of all hash join keys, // the second hash join child is always required to match the distribution returned - // by first child + // by the first child. note, that the returned distribution may have different distribution keys + // compared to the requested one, or even have a different distribution kind altogether // Req(N + 1) (hashed, broadcast) // Req(N + 2) (non-singleton, broadcast) // Req(N + 3) (singleton, singleton) diff --git a/src/backend/gporca/libgpopt/src/operators/CPhysicalInnerHashJoin.cpp b/src/backend/gporca/libgpopt/src/operators/CPhysicalInnerHashJoin.cpp index ae4ce941684d..e9f8b605806a 100644 --- a/src/backend/gporca/libgpopt/src/operators/CPhysicalInnerHashJoin.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CPhysicalInnerHashJoin.cpp @@ -14,6 +14,7 @@ #include "gpos/base.h" #include "gpopt/base/CDistributionSpecHashed.h" +#include "gpopt/base/COptimizationContext.h" #include "gpopt/base/CUtils.h" #include "gpopt/operators/CExpressionHandle.h" @@ -68,8 +69,10 @@ CPhysicalInnerHashJoin::PdshashedCreateMatching( { GPOS_ASSERT(NULL != pdshashed); - CDistributionSpecHashed *pdshashedMatching = - PdshashedMatching(mp, pdshashed, ulSourceChild); + // if we end up here, it is completely unknown how our child derived its distribution + // so tell the function that we don't know how the spec was derived, and hope for the best + CDistributionSpecHashed *pdshashedMatching = PdshashedMatching( + mp, pdshashed, ulSourceChild, GPOPT_INVALID_OPT_REQUEST); // create a new spec with input and the output spec as equivalents, as you don't want to lose // the already existing equivalent specs of pdshashed diff --git a/src/test/regress/expected/qp_correlated_query.out b/src/test/regress/expected/qp_correlated_query.out index 2d5c833835e3..669d67a71063 100644 --- a/src/test/regress/expected/qp_correlated_query.out +++ b/src/test/regress/expected/qp_correlated_query.out @@ -4137,6 +4137,47 @@ SELECT id FROM offers WHERE EXISTS ( RESET optimizer_enforce_subplans; DROP TABLE offers; DROP TABLE contacts; +-------------------------------------------------------------------------------- +-- Test: ORCA-specific behavior related to distribution requests. +-- If a join condition has an 'AND' clause and multiple subexpressions that have +-- the same right-hand side, distribution requests to the outer table +-- can be lost, making it erroneously require a redistribute motion. +-- For more info, please refer to the comments inside +-- CPhysicalJoin::PdshashedMatching +-------------------------------------------------------------------------------- +EXPLAIN +SELECT * +FROM a +WHERE NOT EXISTS ( + SELECT * + FROM B + WHERE (a.j = b.i AND a.i = b.i) +); + QUERY PLAN +---------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) (cost=3.15..5.26 rows=4 width=8) + -> Hash Anti Join (cost=3.15..5.26 rows=2 width=8) + Hash Cond: ((a.j = b.i) AND (a.i = b.i)) + -> Seq Scan on a (cost=0.00..2.05 rows=2 width=8) + -> Hash (cost=3.06..3.06 rows=2 width=4) + -> Seq Scan on b (cost=0.00..3.06 rows=2 width=4) + Optimizer: Postgres query optimizer +(7 rows) + +SELECT * +FROM a +WHERE NOT EXISTS ( + SELECT * + FROM B + WHERE (a.j = b.i AND a.i = b.i) +); + i | j +----+---- + 19 | 5 + 78 | -1 + 99 | 62 +(3 rows) + -- ---------------------------------------------------------------------- -- Test: teardown.sql -- ---------------------------------------------------------------------- diff --git a/src/test/regress/expected/qp_correlated_query_optimizer.out b/src/test/regress/expected/qp_correlated_query_optimizer.out index cd49a080494a..a85df6c6b37b 100644 --- a/src/test/regress/expected/qp_correlated_query_optimizer.out +++ b/src/test/regress/expected/qp_correlated_query_optimizer.out @@ -1470,29 +1470,25 @@ select A.i from A where not exists (select B.i from B where B.i in (select C.i f (3 rows) explain select * from B where not exists (select * from C,A where C.i in (select C.i from C where C.i = A.i and C.i != 10) AND B.i = C.i); - QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------- - Gather Motion 3:1 (slice4; segments: 3) (cost=0.00..1324895.10 rows=1 width=8) + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) (cost=0.00..1324895.10 rows=1 width=8) -> Hash Anti Join (cost=0.00..1324895.10 rows=1 width=8) - Hash Cond: b.i = c.i + Hash Cond: (b.i = c.i) -> Seq Scan on b (cost=0.00..431.00 rows=2 width=8) - -> Hash (cost=1324464.10..1324464.10 rows=13 width=4) - -> Redistribute Motion 3:3 (slice3; segments: 3) (cost=0.00..1324464.10 rows=13 width=4) - Hash Key: c.i - -> Hash Semi Join (cost=0.00..1324464.10 rows=13 width=4) - Hash Cond: a.i = c_1.i AND c.i = c_1.i - -> Redistribute Motion 3:3 (slice2; segments: 3) (cost=0.00..1324033.09 rows=15 width=8) - Hash Key: a.i - -> Nested Loop (cost=0.00..1324033.09 rows=15 width=8) - Join Filter: true - -> Broadcast Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=5 width=4) - -> Seq Scan on a (cost=0.00..431.00 rows=2 width=4) - -> Seq Scan on c (cost=0.00..431.00 rows=3 width=4) - -> Hash (cost=431.00..431.00 rows=3 width=4) - -> Seq Scan on c c_1 (cost=0.00..431.00 rows=3 width=4) - Filter: i <> 10 - Optimizer: Pivotal Optimizer (GPORCA) version 2.74.0 -(20 rows) + -> Hash (cost=1324464.10..1324464.10 rows=3 width=4) + -> Hash Semi Join (cost=0.00..1324464.10 rows=3 width=4) + Hash Cond: ((a.i = c_1.i) AND (c.i = c_1.i)) + -> Nested Loop (cost=0.00..1324033.09 rows=15 width=8) + Join Filter: true + -> Broadcast Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=5 width=4) + -> Seq Scan on a (cost=0.00..431.00 rows=2 width=4) + -> Seq Scan on c (cost=0.00..431.00 rows=3 width=4) + -> Hash (cost=431.00..431.00 rows=3 width=4) + -> Seq Scan on c c_1 (cost=0.00..431.00 rows=3 width=4) + Filter: ((i <> 10) AND ((i < 10) OR (i > 10))) + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) select * from B where not exists (select * from C,A where C.i in (select C.i from C where C.i = A.i and C.i != 10) AND B.i = C.i); i | j @@ -4402,6 +4398,47 @@ SELECT id FROM offers WHERE EXISTS ( RESET optimizer_enforce_subplans; DROP TABLE offers; DROP TABLE contacts; +-------------------------------------------------------------------------------- +-- Test: ORCA-specific behavior related to distribution requests. +-- If a join condition has an 'AND' clause and multiple subexpressions that have +-- the same right-hand side, distribution requests to the outer table +-- can be lost, making it erroneously require a redistribute motion. +-- For more info, please refer to the comments inside +-- CPhysicalJoin::PdshashedMatching +-------------------------------------------------------------------------------- +EXPLAIN +SELECT * +FROM a +WHERE NOT EXISTS ( + SELECT * + FROM B + WHERE (a.j = b.i AND a.i = b.i) +); + QUERY PLAN +------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..862.00 rows=2 width=8) + -> Hash Anti Join (cost=0.00..862.00 rows=1 width=8) + Hash Cond: ((a.j = b.i) AND (a.i = b.i)) + -> Seq Scan on a (cost=0.00..431.00 rows=2 width=8) + -> Hash (cost=431.00..431.00 rows=2 width=4) + -> Seq Scan on b (cost=0.00..431.00 rows=2 width=4) + Optimizer: Pivotal Optimizer (GPORCA) +(7 rows) + +SELECT * +FROM a +WHERE NOT EXISTS ( + SELECT * + FROM B + WHERE (a.j = b.i AND a.i = b.i) +); + i | j +----+---- + 19 | 5 + 78 | -1 + 99 | 62 +(3 rows) + -- ---------------------------------------------------------------------- -- Test: teardown.sql -- ---------------------------------------------------------------------- diff --git a/src/test/regress/expected/update_gp_optimizer.out b/src/test/regress/expected/update_gp_optimizer.out index a203f7f99b2e..a13d74df979c 100644 --- a/src/test/regress/expected/update_gp_optimizer.out +++ b/src/test/regress/expected/update_gp_optimizer.out @@ -122,45 +122,49 @@ EXPLAIN (COSTS OFF) UPDATE keo1 SET user_vie_act_cntr_marg_cum = 234.682 FROM (SELECT min (keo4.keo_para_budget_date) FROM keo4))) ) t1 WHERE t1.user_vie_project_code_pk = keo1.user_vie_project_code_pk; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Update - -> Split - -> Result - -> Hash Join - Hash Cond: ((keo1_1.user_vie_project_code_pk)::text = (keo2.projects_pk)::text) + -> Explicit Redistribute Motion 3:3 (slice8; segments: 3) + -> Split + -> Result -> Hash Join - Hash Cond: ((keo1.user_vie_project_code_pk)::text = (keo1_1.user_vie_project_code_pk)::text) - -> Seq Scan on keo1 + Hash Cond: ((keo1_1.user_vie_project_code_pk)::text = (keo2.projects_pk)::text) + -> Redistribute Motion 3:3 (slice6; segments: 3) + Hash Key: keo1.user_vie_project_code_pk + -> Hash Join + Hash Cond: ((keo1.user_vie_project_code_pk)::text = (keo1_1.user_vie_project_code_pk)::text) + -> Seq Scan on keo1 + -> Hash + -> Broadcast Motion 1:3 (slice5; segments: 1) + -> Hash Join + Hash Cond: ((keo1_1.user_vie_fiscal_year_period_sk)::text = (max((keo3.sky_per)::text))) + -> Gather Motion 3:1 (slice1; segments: 3) + -> Seq Scan on keo1 keo1_1 + -> Hash + -> Aggregate + -> Hash Join + Hash Cond: ((keo3.bky_per)::text = (keo4.keo_para_required_period)::text) + -> Gather Motion 3:1 (slice2; segments: 3) + -> Seq Scan on keo3 + -> Hash + -> Assert + Assert Cond: ((row_number() OVER (?)) = 1) + -> WindowAgg + -> Hash Join + Hash Cond: ((keo4.keo_para_budget_date)::text = (min((keo4_1.keo_para_budget_date)::text))) + -> Gather Motion 3:1 (slice3; segments: 3) + -> Seq Scan on keo4 + -> Hash + -> Aggregate + -> Gather Motion 3:1 (slice4; segments: 3) + -> Seq Scan on keo4 keo4_1 -> Hash - -> Broadcast Motion 1:3 (slice5; segments: 1) - -> Hash Join - Hash Cond: ((keo1_1.user_vie_fiscal_year_period_sk)::text = (max((keo3.sky_per)::text))) - -> Gather Motion 3:1 (slice1; segments: 3) - -> Seq Scan on keo1 keo1_1 - -> Hash - -> Aggregate - -> Hash Join - Hash Cond: ((keo3.bky_per)::text = (keo4.keo_para_required_period)::text) - -> Gather Motion 3:1 (slice2; segments: 3) - -> Seq Scan on keo3 - -> Hash - -> Assert - Assert Cond: ((row_number() OVER (?)) = 1) - -> WindowAgg - -> Hash Join - Hash Cond: ((keo4.keo_para_budget_date)::text = (min((keo4_1.keo_para_budget_date)::text))) - -> Gather Motion 3:1 (slice3; segments: 3) - -> Seq Scan on keo4 - -> Hash - -> Aggregate - -> Gather Motion 3:1 (slice4; segments: 3) - -> Seq Scan on keo4 keo4_1 - -> Hash - -> Broadcast Motion 3:3 (slice6; segments: 3) - -> Seq Scan on keo2 + -> Redistribute Motion 3:3 (slice7; segments: 3) + Hash Key: keo2.projects_pk + -> Seq Scan on keo2 Optimizer: Pivotal Optimizer (GPORCA) -(36 rows) +(40 rows) UPDATE keo1 SET user_vie_act_cntr_marg_cum = 234.682 FROM ( SELECT a.user_vie_project_code_pk FROM keo1 a INNER JOIN keo2 b diff --git a/src/test/regress/sql/qp_correlated_query.sql b/src/test/regress/sql/qp_correlated_query.sql index 76508aaa4d65..fbb745751454 100644 --- a/src/test/regress/sql/qp_correlated_query.sql +++ b/src/test/regress/sql/qp_correlated_query.sql @@ -979,6 +979,32 @@ SELECT id FROM offers WHERE EXISTS ( RESET optimizer_enforce_subplans; DROP TABLE offers; DROP TABLE contacts; +-------------------------------------------------------------------------------- +-- Test: ORCA-specific behavior related to distribution requests. +-- If a join condition has an 'AND' clause and multiple subexpressions that have +-- the same right-hand side, distribution requests to the outer table +-- can be lost, making it erroneously require a redistribute motion. +-- For more info, please refer to the comments inside +-- CPhysicalJoin::PdshashedMatching +-------------------------------------------------------------------------------- +EXPLAIN +SELECT * +FROM a +WHERE NOT EXISTS ( + SELECT * + FROM B + WHERE (a.j = b.i AND a.i = b.i) +); + +SELECT * +FROM a +WHERE NOT EXISTS ( + SELECT * + FROM B + WHERE (a.j = b.i AND a.i = b.i) +); + + -- ---------------------------------------------------------------------- -- Test: teardown.sql -- ---------------------------------------------------------------------- From 6955445c69f80562a8afd68c428e1a697b972a3f Mon Sep 17 00:00:00 2001 From: Georgy Shelkovy Date: Sat, 21 Feb 2026 07:55:34 +0500 Subject: [PATCH 078/111] Fix gpssh-exkeys behave tests (#211) The gpssh_exkeys behave tests passed. However, they did't check anything because all scenarios were skipped. This is due to a mismatch between the file name and the tag name. In other words, we're running tests with a tag based on the file name, but there's no such tag. 1) Rename the gpssh_exkeys.feature file to gpssh-exkeys.feature to ensure an exact match between the file name and the tag name. 2) In the Docker container, the /dev/stdout and /dev/stderr files are not directly writable by an unprivileged user. Capture stdout/stderr via run_cmd. 3) Some tests require each host to have its own files in the /home/gpadmin/.ssh directory because the tests delete them, but not on all hosts. Mount the ssh_keys directory as a whole, not individual files, and mount it to /home/gpadmin/.ssh.src and before running the tests, copy .ssh.src to .ssh so that each host has its own copy of the (initially identical) files. 4) Add -x flag when calling the set command to make it clear in the logs which commands were run. Ticket: GG-121 Co-authored-by: Maxim Michkov --- ci/docker-compose.yaml | 21 +++++++------------ ci/scripts/init_containers.sh | 4 +++- ...sh_exkeys.feature => gpssh-exkeys.feature} | 0 .../steps/gpssh_exkeys_mgmt_utils.py | 2 ++ 4 files changed, 12 insertions(+), 15 deletions(-) rename gpMgmt/test/behave/mgmt_utils/{gpssh_exkeys.feature => gpssh-exkeys.feature} (100%) diff --git a/ci/docker-compose.yaml b/ci/docker-compose.yaml index 48bd4945bc34..5d98e5a567ec 100644 --- a/ci/docker-compose.yaml +++ b/ci/docker-compose.yaml @@ -16,48 +16,41 @@ services: hostname: cdw volumes: - ${PWD}/allure-results:/tmp/allure-results - - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub - - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/ssh_keys:/home/gpadmin/.ssh.src - ${PWD}/logs_cdw:/logs sdw1: <<: *common-service hostname: sdw1 volumes: - - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub - - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/ssh_keys:/home/gpadmin/.ssh.src - ${PWD}/logs_sdw1:/logs sdw2: <<: *common-service hostname: sdw2 volumes: - - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub - - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/ssh_keys:/home/gpadmin/.ssh.src - ${PWD}/logs_sdw2:/logs sdw3: <<: *common-service hostname: sdw3 volumes: - - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub - - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/ssh_keys:/home/gpadmin/.ssh.src - ${PWD}/logs_sdw3:/logs sdw4: <<: *common-service hostname: sdw4 volumes: - - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub - - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/ssh_keys:/home/gpadmin/.ssh.src - ${PWD}/logs_sdw4:/logs sdw5: <<: *common-service hostname: sdw5 volumes: - - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub - - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/ssh_keys:/home/gpadmin/.ssh.src - ${PWD}/logs_sdw5:/logs sdw6: <<: *common-service hostname: sdw6 volumes: - - ${PWD}/ssh_keys/id_rsa.pub:/home/gpadmin/.ssh/id_rsa.pub - - ${PWD}/ssh_keys/id_rsa:/home/gpadmin/.ssh/id_rsa + - ${PWD}/ssh_keys:/home/gpadmin/.ssh.src - ${PWD}/logs_sdw6:/logs diff --git a/ci/scripts/init_containers.sh b/ci/scripts/init_containers.sh index a54d2dafc91e..a5c65faf7153 100644 --- a/ci/scripts/init_containers.sh +++ b/ci/scripts/init_containers.sh @@ -1,5 +1,5 @@ #!/bin/bash -set -eo pipefail +set -eox pipefail project="$1" @@ -18,6 +18,8 @@ for service in $services do docker compose -p $project -f ci/docker-compose.yaml exec -T \ $service bash -c "mkdir -p /data/gpdata && chmod -R 777 /data && + # each host should have its own copy of the (initially identical) files in .ssh + cp -rf .ssh.src .ssh && source gpdb_src/concourse/scripts/common.bash && install_gpdb && ./gpdb_src/concourse/scripts/setup_gpadmin_user.bash" & done diff --git a/gpMgmt/test/behave/mgmt_utils/gpssh_exkeys.feature b/gpMgmt/test/behave/mgmt_utils/gpssh-exkeys.feature similarity index 100% rename from gpMgmt/test/behave/mgmt_utils/gpssh_exkeys.feature rename to gpMgmt/test/behave/mgmt_utils/gpssh-exkeys.feature diff --git a/gpMgmt/test/behave/mgmt_utils/steps/gpssh_exkeys_mgmt_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/gpssh_exkeys_mgmt_utils.py index 8690aa61ed4b..d92a530c82ef 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/gpssh_exkeys_mgmt_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/gpssh_exkeys_mgmt_utils.py @@ -57,6 +57,8 @@ def run_exkeys(hosts, capture=False): subprocess.check_call(args) return + if os.path.exists('/.dockerenv'): + return run_cmd(' '.join(args)) # Capture stdout/err for later use, while routing it through tee(1) so that # developers can still see the live stream output. # From 31fd227225a85e9155172906ff7c83517cb08b91 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Tue, 24 Feb 2026 18:26:10 +0700 Subject: [PATCH 079/111] Command injection vulnerability in Greengage SQL Dump workflow (#276) - Fix command injection in SQL dump workflow Pass `github.event.workflow_run.*` values through `env` section and use heredoc instead of direct echo interpolation. Prevents shell interpretation of backticks and $() in commit messages, branch names, and author names - Apply suggestion from @RekGRpth Use GitHubActions-like variables names Co-authored-by: Georgy Shelkovy Task: ADBDEV-9281 --- .github/workflows/greengage-sql-dump.yml | 26 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/workflows/greengage-sql-dump.yml b/.github/workflows/greengage-sql-dump.yml index 54b68ab81ea6..9c069189d0dd 100644 --- a/.github/workflows/greengage-sql-dump.yml +++ b/.github/workflows/greengage-sql-dump.yml @@ -27,16 +27,24 @@ jobs: actions: write # Required for cache and artifact upload steps: - name: Display trigger information + env: + RUN_NAME: ${{ github.event.workflow_run.name }} + RUN_ID: ${{ github.event.workflow_run.id }} + RUN_HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} + RUN_HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + RUN_HEAD_COMMIT_MESSAGE: ${{ github.event.workflow_run.head_commit.message }} + RUN_HEAD_COMMIT_AUTHOR: ${{ github.event.workflow_run.head_commit.author.name }} run: | - echo "" - echo "================== Workflow Trigger Information ==================" - echo "Triggered by: ${{ github.event.workflow_run.name }} (#${{ github.event.workflow_run.id }})" - echo "Target branch: ${{ github.event.workflow_run.head_branch }}" - echo "Source commit: ${{ github.event.workflow_run.head_sha }}" - echo "Commit message: ${{ github.event.workflow_run.head_commit.message }}" - echo "" - echo "Author: ${{ github.event.workflow_run.head_commit.author.name }}" - echo "==================================================================" + cat << EOF + ================== Workflow Trigger Information ================== + Triggered by: ${RUN_NAME} (#${RUN_ID}) + Target branch: ${RUN_HEAD_BRANCH} + Source commit: ${RUN_HEAD_SHA} + Commit message: ${RUN_HEAD_COMMIT_MESSAGE} + + Author: ${RUN_HEAD_COMMIT_AUTHOR} + ================================================================== + EOF - name: Maximize disk space uses: greengagedb/greengage-ci/.github/actions/maximize-disk-space@v19 From 14d88e513748a226500ad69ba0bac070f7810da1 Mon Sep 17 00:00:00 2001 From: Asim R P Date: Fri, 13 Nov 2020 14:02:23 +0530 Subject: [PATCH 080/111] Extract common parts from isolation2 makefile into global makefile It makes isolation2 framework more consistent with pg_regress and isolation in terms of how it is invoked from the build system. Changes from original commit: 1. More test suites updated, including temp_tables_stat extension. 2. --bindir is replaced with --psqldir. (cherry picked from commit 085c3b25f4bd3ac483b629c22c72d3afe866838b) --- gpcontrib/temp_tables_stat/Makefile | 6 +----- src/Makefile.global.in | 5 +++++ src/test/isolation2/Makefile | 12 ++++++------ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/gpcontrib/temp_tables_stat/Makefile b/gpcontrib/temp_tables_stat/Makefile index eb91caa0742e..ce046e7a0bad 100644 --- a/gpcontrib/temp_tables_stat/Makefile +++ b/gpcontrib/temp_tables_stat/Makefile @@ -21,8 +21,4 @@ endif installcheck: $(MAKE) -C $(top_srcdir)/src/test/isolation2 install - $(top_srcdir)/src/test/isolation2/pg_isolation2_regress \ - $(EXTRA_REGRESS_OPTS) \ - --inputdir=$(srcdir) \ - --dbname=temp_tables_stat_isolation \ - temp_tables_stat + $(pg_isolation2_regress_installcheck) --dbname=temp_tables_stat_isolation temp_tables_stat diff --git a/src/Makefile.global.in b/src/Makefile.global.in index ade6431989a7..9cbbbcdd5204 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -619,6 +619,11 @@ pg_regress_clean_files = results/ regression.diffs regression.out tmp_check/ log pg_isolation_regress_check = $(top_builddir)/src/test/isolation/pg_isolation_regress --inputdir=$(srcdir) --temp-install=./tmp_check --top-builddir=$(top_builddir) $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) pg_isolation_regress_installcheck = $(top_builddir)/src/test/isolation/pg_isolation_regress --inputdir=$(srcdir) --top-builddir=$(top_builddir) $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) +pg_isolation2_regress_installcheck = \ + $(top_builddir)/src/test/isolation2/pg_isolation2_regress \ + --inputdir=$(srcdir) \ + --psqldir='$(PSQLDIR)' \ + $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) ########################################################################## # # Customization diff --git a/src/test/isolation2/Makefile b/src/test/isolation2/Makefile index b192a86db3b4..ff4ecef6631a 100644 --- a/src/test/isolation2/Makefile +++ b/src/test/isolation2/Makefile @@ -70,23 +70,23 @@ clean distclean: install: all gpdiff.pl gpstringsubs.pl installcheck: install installcheck-parallel-retrieve-cursor installcheck-ic-tcp installcheck-ic-proxy - ./pg_isolation2_regress $(EXTRA_REGRESS_OPTS) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --psqldir='$(PSQLDIR)' --inputdir=$(srcdir) --schedule=$(srcdir)/isolation2_schedule + $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --schedule=$(srcdir)/isolation2_schedule installcheck-resgroup: install - ./pg_isolation2_regress $(EXTRA_REGRESS_OPTS) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_resgroup --psqldir='$(PSQLDIR)' --inputdir=$(srcdir) --dbname=isolation2resgrouptest --schedule=$(srcdir)/isolation2_resgroup_schedule + $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_resgroup --dbname=isolation2resgrouptest --schedule=$(srcdir)/isolation2_resgroup_schedule installcheck-parallel-retrieve-cursor: install - ./pg_isolation2_regress $(EXTRA_REGRESS_OPTS) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_parallel_retrieve_cursor --psqldir='$(PSQLDIR)' --inputdir=$(srcdir) --dbname=isolation2parallelretrcursor --load-extension=gp_inject_fault --schedule=$(srcdir)/parallel_retrieve_cursor_schedule + $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_parallel_retrieve_cursor --dbname=isolation2parallelretrcursor --load-extension=gp_inject_fault --schedule=$(srcdir)/parallel_retrieve_cursor_schedule installcheck-mirrorless: install - ./pg_isolation2_regress $(EXTRA_REGRESS_OPTS) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --schedule=$(srcdir)/mirrorless_schedule --dbname=isolation2-mirrorless --load-extension=gp_inject_fault + $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --schedule=$(srcdir)/mirrorless_schedule --dbname=isolation2-mirrorless --load-extension=gp_inject_fault installcheck-ic-tcp: install ifeq ($(findstring gp_interconnect_type=tcp,$(PGOPTIONS)),gp_interconnect_type=tcp) - ./pg_isolation2_regress $(EXTRA_REGRESS_OPTS) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --psqldir='$(PSQLDIR)' --inputdir=$(srcdir) --load-extension=gp_inject_fault --schedule=$(srcdir)/isolation2_ic_tcp_schedule + $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --load-extension=gp_inject_fault --schedule=$(srcdir)/isolation2_ic_tcp_schedule endif installcheck-ic-proxy: install ifeq ($(findstring gp_interconnect_type=proxy,$(PGOPTIONS)),gp_interconnect_type=proxy) - ./pg_isolation2_regress $(EXTRA_REGRESS_OPTS) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --psqldir='$(PSQLDIR)' --inputdir=$(srcdir) --load-extension=gp_inject_fault --schedule=$(srcdir)/isolation2_ic_proxy_schedule + $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --load-extension=gp_inject_fault --schedule=$(srcdir)/isolation2_ic_proxy_schedule endif From 12b1cc9c549d0f04e616574907acb4d27e0924bb Mon Sep 17 00:00:00 2001 From: Maxim Michkov Date: Tue, 17 Feb 2026 13:56:05 +0300 Subject: [PATCH 081/111] Fix isolation2 tests overwriting diffs (#268) Isolation tests run as separate make rules, all of them outputting to the same directory. Because of this, regression.diffs file gets overwritten, and so its contents for earlier runs are lost. To fix this, use --outputdir option to put them all into separate directories, together with results directory, and also auto-generated sql and results. pg_regress also had an issue that it created these directories in the current folder, not in --outputdir, which is also fixed here for --outputdir to work properly. Changes from original commit: 1. There are less test suites in 6X, unnecessary changes removed. 2. Update path to resgroup tests output in run_resgroup_test.bash. 3. Add missing directory creation. 4. Update hardcoded .so path in resgroup tests Ticket: GG-215 (cherry picked from commit 5a495764fe5886d924d68890f86838b7af5c9cce) --- ci/scripts/run_resgroup_test.bash | 4 ++-- src/test/isolation2/.gitignore | 5 +++++ src/test/isolation2/Makefile | 10 +++++----- .../input/resgroup/resgroup_alter_memory.source | 2 +- .../isolation2/input/resgroup/resgroup_bypass.source | 2 +- .../input/resgroup/resgroup_memory_limit.source | 2 +- .../input/resgroup/resgroup_memory_runaway.source | 2 +- .../input/resgroup/resgroup_memory_statistic.source | 2 +- .../input/resgroup/resgroup_move_query.source | 2 +- .../input/resgroup/resgroup_startup_memory.source | 4 ++-- .../output/resgroup/resgroup_alter_memory.source | 2 +- .../isolation2/output/resgroup/resgroup_bypass.source | 2 +- .../output/resgroup/resgroup_memory_limit.source | 2 +- .../output/resgroup/resgroup_memory_runaway.source | 2 +- .../output/resgroup/resgroup_memory_statistic.source | 2 +- .../output/resgroup/resgroup_move_query.source | 2 +- .../output/resgroup/resgroup_startup_memory.source | 4 ++-- src/test/regress/pg_regress.c | 10 ++++++++-- 18 files changed, 36 insertions(+), 25 deletions(-) diff --git a/ci/scripts/run_resgroup_test.bash b/ci/scripts/run_resgroup_test.bash index 629652f5cc24..1cc66d9dddff 100755 --- a/ci/scripts/run_resgroup_test.bash +++ b/ci/scripts/run_resgroup_test.bash @@ -55,7 +55,7 @@ docker compose -p $project -f ci/docker-compose.yaml exec -Tu gpadmin cdw bash - ${CONFIGURE_FLAGS} make -C /home/gpadmin/gpdb_src/src/test/regress - ssh sdw1 mkdir -p /home/gpadmin/gpdb_src/src/test/{regress,isolation2} Date: Thu, 26 Feb 2026 13:10:48 +0700 Subject: [PATCH 082/111] [6X] Resgroup tests don't run due to docker version error (#284) * Retarget resgroup tests to v23 CI tag * Set environment variable for force Docker client API version usage DOCKER_API_VERSION=1.41 Task: CI-5305 --- .github/workflows/greengage-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index 5a44c3319d7b..7a2b689183a8 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -95,7 +95,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-resgroup.yml@v13 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-resgroup.yml@v23 with: version: 6 target_os: ${{ matrix.target_os }} From cc2a3e236b52454fcd2844d25313872881ad2bed Mon Sep 17 00:00:00 2001 From: David Kimura Date: Wed, 16 Dec 2020 01:47:14 +0000 Subject: [PATCH 083/111] Fix minirepro/gpsd os.getlogin() issue https://bugs.python.org/issue584566 On CI machines `os.getlogin()` failed with: ``` Traceback (most recent call last): File "", line 1, in OSError: [Errno 6] No such device or address ``` Changes to the original commit: In gpsd utility we do not support using environmental variables for getting values of parameters, so we don't need `'PGUSER' in envOpts`. Co-authored-by: Soumyadeep Chakraborty (cherry picked from commit 241672bb758774b29af7c791b8615526551edccf) --- gpMgmt/bin/gpsd | 3 ++- gpMgmt/bin/minirepro | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/gpMgmt/bin/gpsd b/gpMgmt/bin/gpsd index 7bd1c735b416..8982b9ec577c 100755 --- a/gpMgmt/bin/gpsd +++ b/gpMgmt/bin/gpsd @@ -6,6 +6,7 @@ import datetime import subprocess import os import platform +import pwd import sys import re from contextlib import closing @@ -208,7 +209,7 @@ def main(): # OK - now let's setup all the arguments & options db = args[0] host = options.host or platform.node() - user = options.user or os.getlogin() + user = options.user or pwd.getpwuid(os.geteuid())[0] port = options.port or '5432' inclSchema = options.dumpSchema inclHLL = options.dumpHLL diff --git a/gpMgmt/bin/minirepro b/gpMgmt/bin/minirepro index 153a55ae5bb7..839bbc674b55 100755 --- a/gpMgmt/bin/minirepro +++ b/gpMgmt/bin/minirepro @@ -59,6 +59,7 @@ EXAMPLE minirepro gptest -h locahost -U gpadmin -p 4444 -q ~/in.sql -f ~/out.sql ''' +import pwd import os, sys, re, json, platform, subprocess from optparse import OptionParser from pygresql import pgdb @@ -331,7 +332,7 @@ def main(): envOpts = os.environ db = args[0] host = options.host or platform.node() - user = options.user or ('PGUSER' in envOpts and envOpts['PGUSER']) or os.getlogin() + user = options.user or ('PGUSER' in envOpts and envOpts['PGUSER']) or pwd.getpwuid(os.geteuid())[0] port = options.port or ('PGPORT' in envOpts and envOpts['PGPORT']) or '5432' query_file = options.query_file output_file = options.output_file From 492cb0bb46f6fa152d3616aeee3a6bcc3ece4442 Mon Sep 17 00:00:00 2001 From: Xing Guo Date: Tue, 12 Sep 2023 09:26:47 +0800 Subject: [PATCH 084/111] Fix minirepro behave tests. (#16406) minirepro's behave tests are diabled on our CI and cannot pass. This patch fixes them. Steps to run minirepro's behave tests: ``` cd gpMgmt make behave flags='--tags=minirepro' ``` These tests will be enabled in a follow-up PR. Changes from original commit: 1. We need not to add anything in `environment.py` as it already has analyze. 2. Minirepro hadn't change it's error message, so we need not to adapt an as 1. (cherry picked from commit 69c306a44e59018f51fce201e07f725c7b98ef2e) --- .../test/behave/mgmt_utils/minirepro.feature | 54 ++++++++++--------- .../mgmt_utils/steps/minirepro_mgmt_utils.py | 14 ++--- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/gpMgmt/test/behave/mgmt_utils/minirepro.feature b/gpMgmt/test/behave/mgmt_utils/minirepro.feature index e5f8c2e80322..db5194f84294 100644 --- a/gpMgmt/test/behave/mgmt_utils/minirepro.feature +++ b/gpMgmt/test/behave/mgmt_utils/minirepro.feature @@ -28,7 +28,9 @@ Feature: Dump minimum database objects that is related to the query @minirepro_UI Scenario: Database does not exist Given database "nonedb000" does not exist - When the user runs "minirepro nonedb000 -q ~/test/in.sql -f ~/out.sql" + # Make sure that minirepro doesn't complains about the missing of in.sql. + Given the file "/tmp/in.sql" exists and contains "select 1;" + When the user runs "minirepro nonedb000 -q /tmp/in.sql -f ~/out.sql" Then minirepro error should contain database "nonedb000" does not exist @minirepro_core @@ -43,8 +45,8 @@ Feature: Dump minimum database objects that is related to the query Given the file "/tmp/in.sql" exists and contains "select * from t1; delete from t2;" When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t1" - And the output file "/tmp/out.sql" should contain "CREATE TABLE t2" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t1" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t2" And the output file "/tmp/out.sql" should contain "WHERE relname = 't1'" And the output file "/tmp/out.sql" should contain "WHERE relname = 't2'" And the output file "/tmp/out.sql" should be loaded to database "minidb_tmp" without error @@ -62,9 +64,9 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t1" before "CREATE TABLE t3" - And the output file "/tmp/out.sql" should contain "CREATE TABLE t3" before "CREATE VIEW v1" - And the output file "/tmp/out.sql" should not contain "CREATE TABLE t2" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t1" before "CREATE TABLE public.t3" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t3" before "CREATE VIEW public.v1" + And the output file "/tmp/out.sql" should not contain "CREATE TABLE public.t2" And the output file "/tmp/out.sql" should contain "WHERE relname = 't1'" And the output file "/tmp/out.sql" should contain "WHERE relname = 't3'" And the output file "/tmp/out.sql" should contain "Table: t1, Attribute: a" @@ -80,12 +82,12 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t1" before "CREATE TABLE t3" - And the output file "/tmp/out.sql" should contain "CREATE TABLE t3" before "CREATE VIEW v1" - And the output file "/tmp/out.sql" should contain "CREATE TABLE t2" before "CREATE TABLE t3" - And the output file "/tmp/out.sql" should contain "CREATE TABLE t3" before "CREATE VIEW v2" - And the output file "/tmp/out.sql" should contain "CREATE VIEW v1" before "CREATE VIEW v3" - And the output file "/tmp/out.sql" should contain "CREATE VIEW v2" before "CREATE VIEW v3" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t1" before "CREATE TABLE public.t3" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t3" before "CREATE VIEW public.v1" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t2" before "CREATE TABLE public.t3" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t3" before "CREATE VIEW public.v2" + And the output file "/tmp/out.sql" should contain "CREATE VIEW public.v1" before "CREATE VIEW public.v3" + And the output file "/tmp/out.sql" should contain "CREATE VIEW public.v2" before "CREATE VIEW public.v3" And the output file "/tmp/out.sql" should contain "WHERE relname = 't1'" And the output file "/tmp/out.sql" should contain "WHERE relname = 't2'" And the output file "/tmp/out.sql" should contain "WHERE relname = 't3'" @@ -104,7 +106,7 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t1" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t1" And the output file "/tmp/out.sql" should contain "WHERE relname = 't1'" And the output file "/tmp/out.sql" should contain "Table: t1, Attribute: a" And the output file "/tmp/out.sql" should contain "Table: t1, Attribute: b" @@ -117,7 +119,7 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t2" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t2" And the output file "/tmp/out.sql" should contain "WHERE relname = 't2'" And the output file "/tmp/out.sql" should contain "Table: t2, Attribute: c" And the output file "/tmp/out.sql" should contain "Table: t2, Attribute: d" @@ -130,7 +132,7 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t3" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t3" And the output file "/tmp/out.sql" should contain "WHERE relname = 't3'" And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: e" And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: f" @@ -143,7 +145,7 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should not contain "CREATE TABLE t0" + And the output file "/tmp/out.sql" should not contain "CREATE TABLE public.t0" And the output file "/tmp/out.sql" should be loaded to database "minidb_tmp" without error And the file "/tmp/in.sql" should be executed in database "minidb_tmp" without error @@ -153,8 +155,8 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should not contain "CREATE TABLE t0" - And the output file "/tmp/out.sql" should contain "CREATE TABLE t3" + And the output file "/tmp/out.sql" should not contain "CREATE TABLE public.t0" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t3" And the output file "/tmp/out.sql" should contain "WHERE relname = 't3'" And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: e" And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: f" @@ -167,7 +169,7 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t2" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t2" And the output file "/tmp/out.sql" should contain "WHERE relname = 't2'" And the output file "/tmp/out.sql" should contain "Table: t2, Attribute: c" And the output file "/tmp/out.sql" should contain "Table: t2, Attribute: d" @@ -180,7 +182,7 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t1" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t1" And the output file "/tmp/out.sql" should contain "WHERE relname = 't1'" And the output file "/tmp/out.sql" should contain "Table: t1, Attribute: a" And the output file "/tmp/out.sql" should contain "Table: t1, Attribute: b" @@ -193,7 +195,7 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t3" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t3" And the output file "/tmp/out.sql" should contain "WHERE relname = 't3'" And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: e" And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: f" @@ -206,9 +208,9 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t2" before "CREATE TABLE t3" - And the output file "/tmp/out.sql" should contain "CREATE TABLE t3" before "CREATE VIEW v2" - And the output file "/tmp/out.sql" should not contain "CREATE TABLE t1" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t2" before "CREATE TABLE public.t3" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t3" before "CREATE VIEW public.v2" + And the output file "/tmp/out.sql" should not contain "CREATE TABLE public.t1" And the output file "/tmp/out.sql" should contain "WHERE relname = 't2'" And the output file "/tmp/out.sql" should contain "WHERE relname = 't3'" And the output file "/tmp/out.sql" should contain "Table: t2, Attribute: c" @@ -244,7 +246,7 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t3" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t3" And the output file "/tmp/out.sql" should contain "WHERE relname = 't3'" And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: e" And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: f" @@ -257,7 +259,7 @@ Feature: Dump minimum database objects that is related to the query And the file "/tmp/out.sql" does not exist When the user runs "minirepro minireprodb -q /tmp/in.sql -f /tmp/out.sql" Then the output file "/tmp/out.sql" should exist - And the output file "/tmp/out.sql" should contain "CREATE TABLE t3" + And the output file "/tmp/out.sql" should contain "CREATE TABLE public.t3" And the output file "/tmp/out.sql" should contain "WHERE relname = 't3'" And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: e" And the output file "/tmp/out.sql" should contain "Table: t3, Attribute: f" diff --git a/gpMgmt/test/behave/mgmt_utils/steps/minirepro_mgmt_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/minirepro_mgmt_utils.py index 509a5b450d46..8859c5372523 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/minirepro_mgmt_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/minirepro_mgmt_utils.py @@ -1,5 +1,5 @@ -import os, mmap -from test.behave_utils.utils import drop_database_if_exists, drop_table_if_exists +import os, mmap, re +from test.behave_utils.utils import * @given('database "{dbname}" does not exist') def impl(context, dbname): @@ -18,7 +18,7 @@ def impl(context, file_name, sql_query): if not os.path.exists(file_dir): os.makedirs(file_dir) with open(file_name, 'w') as query_f: - query_f.writelines(sql_query) + query_f.writelines(sql_query) @given('the table "{rel_name}" does not exist in database "{db_name}"') def impl(context, rel_name, db_name): @@ -41,8 +41,8 @@ def impl(context, output_file): def impl(context, output_file, str_before, str_after): with open(output_file, 'r') as output_f: s = mmap.mmap(output_f.fileno(), 0, access=mmap.ACCESS_READ) - pos_before = s.find(str_before) - pos_after = s.find(str_after) + pos_before = s.find(str_before.encode()) + pos_after = s.find(str_after.encode()) if pos_before == -1: raise Exception('%s not found.' % str_before) if pos_after == -1: @@ -54,14 +54,14 @@ def impl(context, output_file, str_before, str_after): def impl(context, output_file, search_str): with open(output_file, 'r') as output_f: s = mmap.mmap(output_f.fileno(), 0, access=mmap.ACCESS_READ) - if s.find(search_str) == -1: + if s.find(search_str.encode()) == -1: raise Exception('%s not found.' % search_str) @then('the output file "{output_file}" should not contain "{search_str}"') def impl(context, output_file, search_str): with open(output_file, 'r') as output_f: s = mmap.mmap(output_f.fileno(), 0, access=mmap.ACCESS_READ) - if s.find(search_str) != -1: + if s.find(search_str.encode()) != -1: raise Exception('%s should not exist.' % search_str) @then('the output file "{output_file}" should be loaded to database "{db_name}" without error') From 838ef178c23a80ae16a0010b2bacd5346912f131 Mon Sep 17 00:00:00 2001 From: Tyler Ramer Date: Wed, 13 May 2020 11:28:38 -0400 Subject: [PATCH 085/111] Refactor dbconn One reason pygresql was previously modified was that it did not handle closing a connection very gracefully. In the process of updating pygresql, we've wrapped the connection it provides with a ClosingConnection function, which should handle gracefully closing the connection when the "with dbconn.connect as conn" syntax is used. This did, however, illustrate issues where a cursor might have been created as the result of a dbconn.execSQL() call, which seems to hold the connection open if not specifically closed. It is therefore necessary to remove the ability to get a cursor from dbconn.execSQL(). To highlight this difference, and to ensure that future calls to this library is easy to use, I've cleaned up and clarified the dbconn execution code, to include the following features. - dbconn.execSQL() closes the cursor as part of the function. It returns no rows - functions dbconn.query() is added, which behaves like dbconn.execSQL() except that it now returns a cursor - function dbconn.execQueryforSingleton() is renamed dconn.querySingleton() - function dbconn.execQueryforSingletonRow() is renamed dconn.queryRow() Changes to the original commit: We needed almost no changes from original commit, but to preserve authorship. So the only thing that is left it is openning and closing connection in `analyzedb_mgmt_utils.py` in "rows are inserted into table". Also connection was removed from context in `create_database_if_not_exists`. Authored-by: Tyler Ramer (cherry picked from commit 330db2305749952e534178f37a0dfb35758e17d7) --- gpMgmt/test/behave/mgmt_utils/steps/analyzedb_mgmt_utils.py | 4 +++- gpMgmt/test/behave_utils/utils.py | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gpMgmt/test/behave/mgmt_utils/steps/analyzedb_mgmt_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/analyzedb_mgmt_utils.py index 2ce4aacce7d6..dc08ebd4ad25 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/analyzedb_mgmt_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/analyzedb_mgmt_utils.py @@ -4,6 +4,7 @@ import time from datetime import datetime, timedelta from gppylib.db import dbconn +from contextlib import closing from test.behave_utils.utils import check_schema_exists, check_table_exists, drop_table_if_exists from behave import given, when, then @@ -196,7 +197,8 @@ def impl(context, qualified_table): @then('{num_rows} rows are inserted into table "{tablename}" in schema "{schemaname}" with column type list "{column_type_list}"') @when('{num_rows} rows are inserted into table "{tablename}" in schema "{schemaname}" with column type list "{column_type_list}"') def impl(context, num_rows, tablename, schemaname, column_type_list): - insert_data_into_table(context.conn, schemaname, tablename, column_type_list, num_rows) + with closing(dbconn.connect(dbconn.DbURL(dbname=context.dbname))) as conn: + insert_data_into_table(conn, schemaname, tablename, column_type_list, num_rows) @given('some data is inserted into table "{tablename}" in schema "{schemaname}" with column type list "{column_type_list}"') @when('some data is inserted into table "{tablename}" in schema "{schemaname}" with column type list "{column_type_list}"') diff --git a/gpMgmt/test/behave_utils/utils.py b/gpMgmt/test/behave_utils/utils.py index 030d6b6d1b5a..fe416c70f451 100644 --- a/gpMgmt/test/behave_utils/utils.py +++ b/gpMgmt/test/behave_utils/utils.py @@ -272,7 +272,6 @@ def create_database_if_not_exists(context, dbname, host=None, port=0, user=None) if not check_db_exists(dbname, host, port, user): create_database(context, dbname, host, port, user) context.dbname = dbname - context.conn = dbconn.connect(dbconn.DbURL(dbname=context.dbname), unsetSearchPath=False) def create_database(context, dbname=None, host=None, port=0, user=None): LOOPS = 10 From 8eef09856d7c6fe4f5a1d862c9394493a93f07f6 Mon Sep 17 00:00:00 2001 From: Shapatin Artem Date: Wed, 25 Feb 2026 19:55:43 +0700 Subject: [PATCH 086/111] Enable minirepro tests This commit turns minirepro tests on. --- gpMgmt/test/behave/mgmt_utils/minirepro.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpMgmt/test/behave/mgmt_utils/minirepro.feature b/gpMgmt/test/behave/mgmt_utils/minirepro.feature index db5194f84294..c461a7bdca3a 100644 --- a/gpMgmt/test/behave/mgmt_utils/minirepro.feature +++ b/gpMgmt/test/behave/mgmt_utils/minirepro.feature @@ -1,4 +1,4 @@ -@minirepro @skip +@minirepro Feature: Dump minimum database objects that is related to the query @minirepro_UI From d76a9a8e9d09b31d960fc819f294aec3aeea5455 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Fri, 27 Feb 2026 13:21:28 +0700 Subject: [PATCH 087/111] Greengage SQL Dump workflow fails unexpected for 6.x (#280) * Fix regression SQL Dump unexpected fail Use `if-then` block code for check `CI` variable Task: ADBDEV-9294 --- concourse/scripts/dumpdb.bash | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/concourse/scripts/dumpdb.bash b/concourse/scripts/dumpdb.bash index 40f0c6536464..5cd300fcfb85 100755 --- a/concourse/scripts/dumpdb.bash +++ b/concourse/scripts/dumpdb.bash @@ -28,4 +28,6 @@ done mkdir -pm 777 sqldump pg_dumpall -f ./sqldump/dump.sql -[ -z "${CI:-}" ] && xz -z ./sqldump/dump.sql +if [[ -v CI ]]; then + xz -z ./sqldump/dump.sql +fi From 3d1c3266dd8ecd0411d65265ec4dfc8ae77f3f90 Mon Sep 17 00:00:00 2001 From: Vasiliy Ivanov Date: Fri, 27 Feb 2026 17:19:08 +0200 Subject: [PATCH 088/111] Fix python functions to get postmaster PID (#285) There was a 11 years old bug in the python function fetching postmaster PID for remote host. The bash command had unescaped $ symbol. As a result part of awk command was interpolated as bash argument before ssh. So awk do nothing (`awk {print }`), full ps output grepped by pid and any part of string could match, e.g. postmaster port. Moreover, even PID could be matched as a substring for longest PIDs. The last problem affected function for local search too. So sometimes gprecoverseg could kill arbitrary postgres processes on the same segment host. We simplify bash command with proper escaping and cover these functions by unit tests Ticket: GG-227 Co-authored-by: Georgy Shelkovy --- gpMgmt/bin/gppylib/commands/gp.py | 12 +-- .../commands/test/unit/test_cluster_gp.py | 78 +++++++++++++++++++ .../commands/test/unit/test_unit_gp.py | 2 +- 3 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 gpMgmt/bin/gppylib/commands/test/unit/test_cluster_gp.py diff --git a/gpMgmt/bin/gppylib/commands/gp.py b/gpMgmt/bin/gppylib/commands/gp.py index 05914ad9c736..29cf57887d46 100644 --- a/gpMgmt/bin/gppylib/commands/gp.py +++ b/gpMgmt/bin/gppylib/commands/gp.py @@ -60,24 +60,26 @@ PGDATABASE_FOR_COMMON_USE= 'postgres' def get_postmaster_pid_locally(datadir): - cmdStr = "ps -ef | grep postgres | grep -v grep | awk '{print $2}' | grep `cat %s/postmaster.pid | head -1` || echo -1" % (datadir) + cmdStr = "ps -p \"$(head -1 %s/postmaster.pid)\" -o comm= -o pid=" % (datadir) name = "get postmaster" cmd = Command(name, cmdStr) try: cmd.run(validateAfter=True) sout = cmd.get_results().stdout.lstrip(' ') - return int(sout.split()[0]) + comm, pid = sout.split() + return int(pid if comm == "postgres" else -1) except: return -1 def getPostmasterPID(hostname, datadir): - cmdStr="echo 'START_CMD_OUTPUT';ps -ef | grep postgres | grep -v grep | awk '{print $2}' | grep \\`cat %s/postmaster.pid | head -1\\` || echo -1" % (datadir) + cmdStr="echo 'START_CMD_OUTPUT';ps -p \"\\$(head -1 %s/postmaster.pid)\" -o comm= -o pid=" % (datadir) name="get postmaster pid" cmd=Command(name,cmdStr,ctxt=REMOTE,remoteHost=hostname) try: cmd.run(validateAfter=True) - sout=cmd.get_results().stdout.lstrip(' ') - return int(sout.split('START_CMD_OUTPUT\n')[1].split()[1]) + sout=cmd.get_results().stdout.lstrip(' ').split('START_CMD_OUTPUT\n')[1] + comm, pid = sout.split() + return int(pid if comm == "postgres" else -1) except: return -1 diff --git a/gpMgmt/bin/gppylib/commands/test/unit/test_cluster_gp.py b/gpMgmt/bin/gppylib/commands/test/unit/test_cluster_gp.py new file mode 100644 index 000000000000..e2e7112bf76d --- /dev/null +++ b/gpMgmt/bin/gppylib/commands/test/unit/test_cluster_gp.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python + +import os +import psutil +import shutil +import tempfile +import unittest + +from gppylib.db import dbconn +from gppylib.commands.gp import getPostmasterPID, get_postmaster_pid_locally + + +class GpCommandTestCase(unittest.TestCase): + def setUp(self): + # Connect to the database pointed to by PGHOST et al. + self.url = dbconn.DbURL() + + with dbconn.connect(self.url) as conn: + result = dbconn.execSQL( + conn, + "SELECT port, datadir FROM gp_segment_configuration" + ).fetchall() + + for port, datadir in result: + if not psutil.pid_exists(port): + # Process with PID as port number doesn't exist + # use it for test + self.port = port + self.datadir = datadir + break + else: + # Unexpectedly all postmaster's ports have corresponding PIDs + self.port = -1 + self.pid = -1 + self.datadir = "" + + + def test_get_postmaster_pid_locally_valid_pid(self): + result = get_postmaster_pid_locally(self.datadir) + self.assertIsInstance(result, int) + self.assertNotEqual(result, -1) + + def test_getPostmasterPID_valid_pid(self): + result = getPostmasterPID("localhost", self.datadir) + self.assertIsInstance(result, int) + self.assertNotEqual(result, -1) + + def test_get_postmaster_pid_locally_nonexistent_pid(self): + if self.port == -1: + self.skipTest("Unexpectedly all postmaster's ports have corresponding PIDs") + + temp_dir = tempfile.mkdtemp() + temp_file_path = os.path.join(temp_dir, "postmaster.pid") + with open(temp_file_path, "w") as f: + f.write(str(self.port)) + + result = get_postmaster_pid_locally(temp_dir) + shutil.rmtree(temp_dir) + self.assertIsInstance(result, int) + self.assertEqual(result, -1, "PID file pid: {}, found pid: {}".format(self.port, result)) + + def test_get_getPostmasterPID_nonexistent_pid(self): + if self.port == -1: + self.skipTest("Unexpectedly all postmaster's ports have corresponding PIDs") + + temp_dir = tempfile.mkdtemp() + temp_file_path = os.path.join(temp_dir, "postmaster.pid") + with open(temp_file_path, "w") as f: + f.write(str(self.port)) + + result = getPostmasterPID("localhost", temp_dir) + shutil.rmtree(temp_dir) + self.assertIsInstance(result, int) + self.assertEqual(result, -1, "PID file pid: {}, found pid: {}".format(self.port, result)) + +#------------------------------- Mainline -------------------------------- +if __name__ == '__main__': + unittest.main() diff --git a/gpMgmt/bin/gppylib/commands/test/unit/test_unit_gp.py b/gpMgmt/bin/gppylib/commands/test/unit/test_unit_gp.py index 3b280307537b..d45db420bb51 100644 --- a/gpMgmt/bin/gppylib/commands/test/unit/test_unit_gp.py +++ b/gpMgmt/bin/gppylib/commands/test/unit/test_unit_gp.py @@ -146,7 +146,7 @@ def test_is_pid_postmaster_pgrep_failed_remote(self, mock1, mock2): self.assertTrue(is_pid_postmaster(datadir, pid, remoteHost=remoteHost)) @patch('gppylib.commands.gp.Command.run') - @patch('gppylib.commands.gp.Command.get_results', return_value=CommandResult(0, "1234", "", True, False)) + @patch('gppylib.commands.gp.Command.get_results', return_value=CommandResult(0, "postgres 1234", "", True, False)) def test_get_postmaster_pid_locally(self, mock1, mock2): self.assertEqual(get_postmaster_pid_locally('/tmp'), 1234) From 7cb037e1b63acd7401f41da2dc7294c92e5cfb51 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Tue, 3 Mar 2026 10:15:48 +0700 Subject: [PATCH 089/111] Greengage SQL Dump workflow fails unexpected for 6.x (#288) Update if condition from '-v' to '-z' Task: ADBDEV-9294 --- concourse/scripts/dumpdb.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concourse/scripts/dumpdb.bash b/concourse/scripts/dumpdb.bash index 5cd300fcfb85..a7d64f1da275 100755 --- a/concourse/scripts/dumpdb.bash +++ b/concourse/scripts/dumpdb.bash @@ -28,6 +28,6 @@ done mkdir -pm 777 sqldump pg_dumpall -f ./sqldump/dump.sql -if [[ -v CI ]]; then +if [[ -z "${CI:-}" ]]; then xz -z ./sqldump/dump.sql fi From 373e68d07014447b19de378b9f3d18a114a8bf34 Mon Sep 17 00:00:00 2001 From: Maxim Michkov Date: Tue, 3 Mar 2026 11:19:31 +0300 Subject: [PATCH 090/111] Fix test_clean_up_failed_segments (#287) Python unit test test_clean_up_failed_segments was not actually validating the output due to typo: `assert_called_once_with` should be used instead of `called_once_with`. Also, the number in the output should be 2: only segments with inplace full synchronization should be cleaned, namely inplace_full1 and inplace_full3. This is confirmed by 2 segments listed in the line above: failed1 and failed3. Ticket: GG-238 --- .../operations/test/unit/test_unit_buildMirrorSegments.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gpMgmt/bin/gppylib/operations/test/unit/test_unit_buildMirrorSegments.py b/gpMgmt/bin/gppylib/operations/test/unit/test_unit_buildMirrorSegments.py index 899d3d898002..8d5e87c1fca4 100644 --- a/gpMgmt/bin/gppylib/operations/test/unit/test_unit_buildMirrorSegments.py +++ b/gpMgmt/bin/gppylib/operations/test/unit/test_unit_buildMirrorSegments.py @@ -475,8 +475,9 @@ def test_clean_up_failed_segments(self): build_mirrors_obj._clean_up_failed_segments() + # Only inplace_full1 and inplace_full2 should be cleaned self.mock_get_segments_by_hostname.assert_called_once_with([failed1, failed3]) - self.mock_logger.info.called_once_with('"Cleaning files from 3 segment(s)') + self.mock_logger.info.assert_called_once_with('Cleaning files from 2 segment(s)') def test_clean_up_failed_segments_no_segs_to_cleanup(self): failed2 = self._create_primary(dbid='3', status='d') From 0d971dcf35ccc05a66a683182bb12b2bd07e1486 Mon Sep 17 00:00:00 2001 From: Maxim Michkov Date: Tue, 3 Mar 2026 14:02:46 +0300 Subject: [PATCH 091/111] Enable gpload test (#294) Tests for gpload were disabled for ubuntu, even though they pass without errors. Remove the ubuntu check to add them to the active test suite. Also move its Python dependencies to be installed during the docker building step, not at runtime. Ticket: ADBDEV-9317 --- ci/Dockerfile.ubuntu | 4 ++++ gpMgmt/bin/gpload_test/Makefile | 14 ++------------ gpMgmt/bin/gpload_test/pytest_requirement.txt | 4 +++- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/ci/Dockerfile.ubuntu b/ci/Dockerfile.ubuntu index 4df067e7127f..b42991b1efd4 100644 --- a/ci/Dockerfile.ubuntu +++ b/ci/Dockerfile.ubuntu @@ -3,6 +3,7 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"] WORKDIR /home/gpadmin COPY README.ubuntu.bash ./ +COPY gpMgmt/bin/gpload_test/pytest_requirement.txt ./ RUN set -eux; \ ./README.ubuntu.bash; \ rm README.ubuntu.bash; \ @@ -19,6 +20,9 @@ RUN set -eux; \ echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf; \ # Install allure-behave for behave tests pip2 install --no-cache-dir allure-behave==2.4.0 && \ +# Install pytest for gpload test + python -m pip install --no-cache-dir -r pytest_requirement.txt && \ + rm pytest_requirement.txt && \ # Cleanup to reduce image size apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/cache/man/ diff --git a/gpMgmt/bin/gpload_test/Makefile b/gpMgmt/bin/gpload_test/Makefile index e10b2991e84e..3d2df02e64da 100644 --- a/gpMgmt/bin/gpload_test/Makefile +++ b/gpMgmt/bin/gpload_test/Makefile @@ -19,10 +19,7 @@ GPTest.pm: .PHONY: installcheck -ifeq "$(findstring ubuntu, $(TEST_OS))" "ubuntu" -installcheck: gpdiff.pl gpstringsubs.pl - @echo "skip gpload test for ubuntu" -else ifeq "$(findstring CentOS release 6., $(OS))" "CentOS release 6." +ifeq "$(findstring CentOS release 6., $(OS))" "CentOS release 6." installcheck: gpdiff.pl gpstringsubs.pl @echo "skip gpload test for centos6" else ifeq "$(findstring CentOS Linux release 7., $(OS))" "CentOS Linux release 7." @@ -33,15 +30,8 @@ installcheck: gpdiff.pl gpstringsubs.pl @echo "skip gpload test for Red Hat" else installcheck: gpdiff.pl gpstringsubs.pl - # install pytest for python2 and gpload test - python2 -m ensurepip --upgrade - python2 -m pip install -r pytest_requirement.txt - python2 -m pip install pytest-order==0.11.0 - python2 -m pip install lxml - @cd gpload && ./TEST.py - @export PYTHONPATH=$PYTHONPATH:/usr/lib/python2.7/site-packages/:/usr/lib64/python2.7/site-packages/:/usr/local/greengage-db-devel/lib/python\ - && cd gpload2 && python2 -m pytest TEST_local_* + @cd gpload2 && python -m pytest TEST_local_* endif clean distclean: diff --git a/gpMgmt/bin/gpload_test/pytest_requirement.txt b/gpMgmt/bin/gpload_test/pytest_requirement.txt index bdec52f5a477..7d686e8a198b 100644 --- a/gpMgmt/bin/gpload_test/pytest_requirement.txt +++ b/gpMgmt/bin/gpload_test/pytest_requirement.txt @@ -7,4 +7,6 @@ more_itertools==5.0.0 packaging==20.9 pluggy==0.13.1 importlib-metadata==2.1.0 -pytest==4.6. \ No newline at end of file +pytest==4.6. +pytest-order==0.11.0 +lxml==5.0.2 \ No newline at end of file From 44e96ec948f9caa7c8c667716a6d6c2d71e29716 Mon Sep 17 00:00:00 2001 From: Maxim Michkov Date: Tue, 3 Mar 2026 17:35:31 +0300 Subject: [PATCH 092/111] Install pg_isolation2_regress for use in extension tests (#274) Previously, it was impossible to run isolation tests without directly building pg_isolation2_regress from source. This complicates testing infrastructure in extensions, requiring to specify ISOLATION2_ROOT to run isolation tests. This patch instead builds and installs pg_isolation2_regress during the compilation step. Regular pg_regress was already built and installed in the pgxs directory, but for some reason it wasn't actually being used. This patch instead uses the installed version of pg_regress, and adds pg_isolation2_regress to the same place. Other necessary files are added as symlinks to regress installation. test_python step is moved from `all` target to `installcheck` target, since it requires installed pygresql to work. Ticket: GG-206 --- gpcontrib/temp_tables_stat/Makefile | 1 - src/Makefile | 1 + src/Makefile.global.in | 4 +-- src/test/isolation2/Makefile | 53 +++++++++++++++-------------- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/gpcontrib/temp_tables_stat/Makefile b/gpcontrib/temp_tables_stat/Makefile index ce046e7a0bad..dddddb810203 100644 --- a/gpcontrib/temp_tables_stat/Makefile +++ b/gpcontrib/temp_tables_stat/Makefile @@ -20,5 +20,4 @@ include $(top_srcdir)/contrib/contrib-global.mk endif installcheck: - $(MAKE) -C $(top_srcdir)/src/test/isolation2 install $(pg_isolation2_regress_installcheck) --dbname=temp_tables_stat_isolation temp_tables_stat diff --git a/src/Makefile b/src/Makefile index 410873b45e06..bf593082d838 100644 --- a/src/Makefile +++ b/src/Makefile @@ -25,6 +25,7 @@ SUBDIRS = \ pl \ makefiles \ test/regress \ + test/isolation2 \ test/perl # There are too many interdependencies between the subdirectories, so diff --git a/src/Makefile.global.in b/src/Makefile.global.in index 9cbbbcdd5204..8080c0a4313a 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -612,7 +612,7 @@ endif pg_regress_locale_flags = $(if $(ENCODING),--encoding=$(ENCODING)) $(NOLOCALE) pg_regress_check = $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --temp-install=./tmp_check --top-builddir=$(top_builddir) $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) -pg_regress_installcheck = $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir='$(PSQLDIR)' $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) +pg_regress_installcheck = $(pgxsdir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir='$(PSQLDIR)' $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) pg_regress_clean_files = results/ regression.diffs regression.out tmp_check/ log/ @@ -620,7 +620,7 @@ pg_isolation_regress_check = $(top_builddir)/src/test/isolation/pg_isolation_reg pg_isolation_regress_installcheck = $(top_builddir)/src/test/isolation/pg_isolation_regress --inputdir=$(srcdir) --top-builddir=$(top_builddir) $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) pg_isolation2_regress_installcheck = \ - $(top_builddir)/src/test/isolation2/pg_isolation2_regress \ + $(pgxsdir)/src/test/isolation2/pg_isolation2_regress \ --inputdir=$(srcdir) \ --psqldir='$(PSQLDIR)' \ $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) diff --git a/src/test/isolation2/Makefile b/src/test/isolation2/Makefile index a3090c543f23..58f156894bf6 100644 --- a/src/test/isolation2/Makefile +++ b/src/test/isolation2/Makefile @@ -20,7 +20,7 @@ endif override CPPFLAGS := -I$(srcdir) -I$(libpq_srcdir) -I$(srcdir)/../regress $(CPPFLAGS) override LDLIBS := $(libpq_pgport) $(LDLIBS) -all: test_python pg_isolation2_regress$(X) all-lib data scan_flaky_fault_injectors test_parallel_retrieve_cursor_extended_query test_parallel_retrieve_cursor_extended_query_error +all: pg_isolation2_regress$(X) all-lib data scan_flaky_fault_injectors test_parallel_retrieve_cursor_extended_query test_parallel_retrieve_cursor_extended_query_error test_python: python helpers_test.py @@ -35,24 +35,11 @@ pg_regress.o: $(MAKE) -C $(top_builddir)/src/test/regress pg_regress.o rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/pg_regress.o . -gpstringsubs.pl: - rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/gpstringsubs.pl - -gpdiff.pl: atmsort.pm explain.pm GPTest.pm - rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/gpdiff.pl - -GPTest.pm: - rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/GPTest.pm - -atmsort.pm: - rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/atmsort.pm - -explain.pm: - rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/explain.pm - data: rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/data +isolation2_regress.o: submake-libpgport + .PHONY: scan_flaky_fault_injectors scan_flaky_fault_injectors: $(top_builddir)/src/test/regress/scan_flaky_fault_injectors.sh @@ -63,30 +50,46 @@ pg_isolation2_regress$(X): isolation2_main.o pg_regress.o submake-libpgport subm clean distclean: rm -f pg_isolation2_regress$(X) $(OBJS) isolation2_main.o isolation2_regress.so rm -f pg_regress.o test_parallel_retrieve_cursor_extended_query test_parallel_retrieve_cursor_extended_query_error - rm -f gpstringsubs.pl gpdiff.pl atmsort.pm explain.pm rm -f data rm -rf $(pg_regress_clean_files) -install: all gpdiff.pl gpstringsubs.pl - -installcheck: install installcheck-parallel-retrieve-cursor installcheck-ic-tcp installcheck-ic-proxy +install: all + $(MKDIR_P) '$(DESTDIR)$(pgxsdir)/$(subdir)' + $(INSTALL_PROGRAM) pg_isolation2_regress$(X) '$(DESTDIR)$(pgxsdir)/$(subdir)/pg_isolation2_regress$(X)' + $(INSTALL_PROGRAM) sql_isolation_testcase.py '$(DESTDIR)$(pgxsdir)/$(subdir)/sql_isolation_testcase.py' + $(LN_S) -f '$(DESTDIR)$(pgxsdir)/src/test/regress/gpdiff.pl' '$(DESTDIR)$(pgxsdir)/$(subdir)/gpdiff.pl' + $(LN_S) -f '$(DESTDIR)$(pgxsdir)/src/test/regress/gpstringsubs.pl' '$(DESTDIR)$(pgxsdir)/$(subdir)/gpstringsubs.pl' + $(LN_S) -f '$(DESTDIR)$(pgxsdir)/src/test/regress/GPTest.pm' '$(DESTDIR)$(pgxsdir)/$(subdir)/GPTest.pm' + $(LN_S) -f '$(DESTDIR)$(pgxsdir)/src/test/regress/atmsort.pm' '$(DESTDIR)$(pgxsdir)/$(subdir)/atmsort.pm' + $(LN_S) -f '$(DESTDIR)$(pgxsdir)/src/test/regress/explain.pm' '$(DESTDIR)$(pgxsdir)/$(subdir)/explain.pm' + +uninstall: + rm -f '$(DESTDIR)$(pgxsdir)/$(subdir)/pg_isolation2_regress$(X)' + rm -f '$(DESTDIR)$(pgxsdir)/$(subdir)/sql_isolation_testcase.py' + rm -f '$(DESTDIR)$(pgxsdir)/$(subdir)/gpdiff.pl' + rm -f '$(DESTDIR)$(pgxsdir)/$(subdir)/gpstringsubs.pl' + rm -f '$(DESTDIR)$(pgxsdir)/$(subdir)/GPTest.pm' + rm -f '$(DESTDIR)$(pgxsdir)/$(subdir)/atmsort.pm' + rm -f '$(DESTDIR)$(pgxsdir)/$(subdir)/explain.pm' + +installcheck: test_python installcheck-parallel-retrieve-cursor installcheck-ic-tcp installcheck-ic-proxy $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --schedule=$(srcdir)/isolation2_schedule -installcheck-resgroup: install +installcheck-resgroup: all-lib data $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_resgroup --dbname=isolation2resgrouptest --outputdir=resgroup --schedule=$(srcdir)/isolation2_resgroup_schedule -installcheck-parallel-retrieve-cursor: install +installcheck-parallel-retrieve-cursor: all-lib data test_parallel_retrieve_cursor_extended_query test_parallel_retrieve_cursor_extended_query_error $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_parallel_retrieve_cursor --dbname=isolation2parallelretrcursor --outputdir=parallelretrcursor --load-extension=gp_inject_fault --schedule=$(srcdir)/parallel_retrieve_cursor_schedule -installcheck-mirrorless: install +installcheck-mirrorless: all-lib data $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --schedule=$(srcdir)/mirrorless_schedule --outputdir=mirrorless --dbname=isolation2-mirrorless --load-extension=gp_inject_fault -installcheck-ic-tcp: install +installcheck-ic-tcp: all-lib data ifeq ($(findstring gp_interconnect_type=tcp,$(PGOPTIONS)),gp_interconnect_type=tcp) $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --outputdir=ic_tcp --load-extension=gp_inject_fault --schedule=$(srcdir)/isolation2_ic_tcp_schedule endif -installcheck-ic-proxy: install +installcheck-ic-proxy: all-lib data ifeq ($(findstring gp_interconnect_type=proxy,$(PGOPTIONS)),gp_interconnect_type=proxy) $(pg_isolation2_regress_installcheck) --init-file=$(top_builddir)/src/test/regress/init_file --init-file=./init_file_isolation2 --outputdir=ic_proxy --load-extension=gp_inject_fault --schedule=$(srcdir)/isolation2_ic_proxy_schedule endif From a3c2e30e6717885cfd35542b02c1d30548754545 Mon Sep 17 00:00:00 2001 From: Ivan Sergeenko Date: Tue, 3 Mar 2026 20:45:43 +0300 Subject: [PATCH 093/111] Prevent ORCA from generating redundant redistributions on joins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When performing a join on types within the same distribution opfamily, there is no need to create a redistribution motion because the hash functions of these types are compatible with each other. ORCA, on the other hand, is not aware of this and creates a redistribution. The easy part of the fix is to detect such cases. The hard part is to integrate this logic into the existing one. It can be done in at least two ways: Inside the distribution matching logic, the current distribution can be safely changed to the desired one, as they are practically equivalent. Alternatively, the actual distribution of the scan can be returned. In this case, every piece of code that expects the derived distribution to directly match the required one should be changed as well. The full-fledged version of the first approach would require adding a new enforcing type to the EPropEnforcingType enum, as well as changing CDistributionSpec::Matches and CDistributionSpec: FSatisfies virtual functions overloads. The enforcing logic inside CDistributionSpecHashed::AppendEnforcers should also be changed. To minimize the amount of changes, only CDistributionSpecHashed::AppendEnforcers function can be modified to decide how to enforce a distribution on its own, but it doesn’t seem to fit into the current logic. The second approach requires thorough investigation to determine every piece of code that should perform distribution matching instead of direct expression matching. Each of these approaches is not trivial. The second one was chosen, because it makes the logic explicit. It allows the expression’s parent to know about the derived distribution, enabling specialized logic if necessary. In this patch, an overload of CUtils::Equals supporting distribution matching is added. Other matching functions are modified to support it as well. Where required, expression-matching versions of these functions are changed to distribution-matching ones. Also, to make the casting logic of the planners easier to observe, a new test file was added. Ticket: GG-208 (cherry picked from commit dd7b460) Changes from the original commit: - Test results were adjusted to account for differences between branches. - Missing drop commands for cst_varchar table were added. - 'optimizer_enable_nljoin' guc was substituted with 'optimizer_nestloop_factor` as the former one it not supported. --- .../minidump/DML-With-WindowFunc-OuterRef.mdp | 59 +- .../LOJ-IndexApply-CompsiteKey-Equiv.mdp | 415 ++++-- ...PushConstantSelectPredicateThruJoin-13.mdp | 82 +- ...PushConstantSelectPredicateThruJoin-14.mdp | 82 +- ...PushConstantSelectPredicateThruJoin-15.mdp | 82 +- .../libgpopt/include/gpopt/base/CUtils.h | 22 +- .../src/base/CDistributionSpecHashed.cpp | 32 +- .../gporca/libgpopt/src/base/CUtils.cpp | 150 +- .../src/operators/CPhysicalHashJoin.cpp | 12 +- .../libgpopt/src/operators/CPhysicalJoin.cpp | 18 + src/test/regress/expected/gp_cast.out | 1207 ++++++++++++++++ .../regress/expected/gp_cast_optimizer.out | 1215 +++++++++++++++++ .../regress/expected/gporca_optimizer.out | 22 +- src/test/regress/expected/join_optimizer.out | 42 +- src/test/regress/greengage_schedule | 2 +- src/test/regress/sql/gp_cast.sql | 271 ++++ 16 files changed, 3329 insertions(+), 384 deletions(-) create mode 100644 src/test/regress/expected/gp_cast.out create mode 100644 src/test/regress/expected/gp_cast_optimizer.out create mode 100644 src/test/regress/sql/gp_cast.sql diff --git a/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp b/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp index 5b2474576323..ba724194cfef 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp @@ -360,10 +360,10 @@ - + - + @@ -377,7 +377,7 @@ - + @@ -391,7 +391,7 @@ - + @@ -402,7 +402,7 @@ - + @@ -513,9 +513,9 @@ - + - + @@ -523,38 +523,19 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/LOJ-IndexApply-CompsiteKey-Equiv.mdp b/src/backend/gporca/data/dxl/minidump/LOJ-IndexApply-CompsiteKey-Equiv.mdp index d5bd415c820c..354cdad1b8e9 100644 --- a/src/backend/gporca/data/dxl/minidump/LOJ-IndexApply-CompsiteKey-Equiv.mdp +++ b/src/backend/gporca/data/dxl/minidump/LOJ-IndexApply-CompsiteKey-Equiv.mdp @@ -5,27 +5,35 @@ Left outer index NL join with multiple distribution keys and the same composite create table foo(a varchar, b int, c int) distributed by(a,b); create table bar(a varchar, b int, c int) distributed by(a,b); create table zoo(a varchar, b int, c int) with(appendonly=true) distributed by(a,b); + +insert into foo (select gen, gen, gen from generate_series(1, 10) gen); +insert into zoo (select gen, gen, gen from generate_series(1, 10) gen); +insert into bar (select gen, gen, gen from generate_series(1, 10) gen); + +analyze foo; +analyze zoo; +analyze bar; + create index bar_idx_ab on bar(b,a); create index zoo_idx_ab on zoo(b,a); set optimizer_enable_hashjoin=off; explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left join zoo on zoo.a=foo.a and zoo.b=foo.b; - QUERY PLAN - Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..1295.00 rows=3 width=48) - -> Nested Loop Left Join (cost=0.00..1295.00 rows=1 width=48) + QUERY PLAN + Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..922.61 rows=10 width=30) + -> Nested Loop Left Join (cost=0.00..922.61 rows=4 width=30) Join Filter: true - -> Nested Loop Left Join (cost=0.00..433.00 rows=1 width=32) + -> Nested Loop Left Join (cost=0.00..491.00 rows=4 width=20) Join Filter: true - -> Table Scan on foo (cost=0.00..431.00 rows=1 width=16) - -> Index Scan using bar_idx_ab on bar (cost=0.00..2.00 rows=1 width=16) - Index Cond: bar.b = foo.b AND bar.a::text = foo.a::text - -> Bitmap Table Scan on zoo (cost=0.00..862.00 rows=1 width=16) - Recheck Cond: zoo.a::text = foo.a::text AND zoo.b = foo.b + -> Seq Scan on foo (cost=0.00..431.00 rows=4 width=10) + -> Index Scan using bar_idx_ab on bar (cost=0.00..60.00 rows=1 width=10) + Index Cond: ((b = foo.b) AND ((a)::text = (foo.a)::text)) + -> Bitmap Heap Scan on zoo (cost=0.00..431.61 rows=1 width=10) + Recheck Cond: (((a)::text = (foo.a)::text) AND (b = foo.b)) -> Bitmap Index Scan on zoo_idx_ab (cost=0.00..0.00 rows=0 width=0) - Index Cond: zoo.b = foo.b AND zoo.a::text = foo.a::text - Settings: enable_nestloop=on; optimizer=on - Optimizer status: PQO version 2.53.11 -(14 rows) + Index Cond: ((b = foo.b) AND ((a)::text = (foo.a)::text)) + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) --> @@ -36,14 +44,16 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + - - + + - + + + @@ -59,6 +69,8 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left + + @@ -73,7 +85,9 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + + + @@ -88,7 +102,65 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -97,13 +169,15 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - - + + - + + + @@ -118,11 +192,8 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - - - - - + + @@ -137,7 +208,8 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + + @@ -152,10 +224,62 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -167,18 +291,6 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - - - - - - - - - - - - @@ -186,14 +298,56 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -224,23 +378,18 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - - - + + + + + - - - - - - - - - + + - + @@ -252,6 +401,18 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left + + + + + + + + + + + + @@ -260,48 +421,54 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -319,9 +486,9 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + - + @@ -335,9 +502,9 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + - + @@ -366,9 +533,9 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + - + @@ -396,7 +563,7 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + @@ -429,9 +596,9 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + - + @@ -466,9 +633,9 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + - + @@ -496,7 +663,7 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + @@ -510,9 +677,9 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + - + @@ -527,7 +694,7 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + @@ -555,10 +722,10 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - - + + - + @@ -571,10 +738,14 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left + + + + - + @@ -619,11 +790,11 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left - + - + - + @@ -632,6 +803,10 @@ explain select * from foo left join bar on foo.a = bar.a and foo.b = bar.b left + + + + diff --git a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-13.mdp b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-13.mdp index 5a757197a70f..d6193d48b723 100644 --- a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-13.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-13.mdp @@ -10,19 +10,18 @@ CREATE TABLE t2 (id smallint); EXPLAIN (costs off) SELECT * FROM t1 a JOIN t2 fa ON a.id = fa.id WHERE a.id = 1; - QUERY PLAN - ------------------------------------------------------------------ + QUERY PLAN + ---------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Hash Join - Hash Cond: (t1.id = (t2.id)::integer) - -> Seq Scan on t1 + Hash Cond: (a.id = (fa.id)::integer) + -> Seq Scan on t1 a Filter: (id = 1) -> Hash - -> Redistribute Motion 3:3 (slice2; segments: 3) - Hash Key: (t2.id)::integer - -> Seq Scan on t2 - Filter: (id = 1) + -> Seq Scan on t2 fa + Filter: (id = 1) Optimizer: Pivotal Optimizer (GPORCA) + (9 rows) ]]> @@ -331,10 +330,10 @@ - + - + @@ -348,7 +347,7 @@ - + @@ -396,53 +395,34 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-14.mdp b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-14.mdp index 9d3ac2bf2b0a..6932b5eaaa7b 100644 --- a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-14.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-14.mdp @@ -8,19 +8,18 @@ CREATE TABLE t2 (id smallint); EXPLAIN (costs off) SELECT * FROM t1 a JOIN t2 fa ON a.id = fa.id WHERE a.id > 1; - QUERY PLAN - ------------------------------------------------------------------ + QUERY PLAN + ---------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Hash Join - Hash Cond: (t1.id = (t2.id)::integer) - -> Seq Scan on t1 + Hash Cond: (a.id = (fa.id)::integer) + -> Seq Scan on t1 a Filter: (id > 1) -> Hash - -> Redistribute Motion 3:3 (slice2; segments: 3) - Hash Key: (t2.id)::integer - -> Seq Scan on t2 - Filter: (id > 1) + -> Seq Scan on t2 fa + Filter: (id > 1) Optimizer: Pivotal Optimizer (GPORCA) + (9 rows) ]]> @@ -378,10 +377,10 @@ - + - + @@ -395,7 +394,7 @@ - + @@ -443,53 +442,34 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-15.mdp b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-15.mdp index 377138d63fe4..37ffaf8f8582 100644 --- a/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-15.mdp +++ b/src/backend/gporca/data/dxl/minidump/PushConstantSelectPredicateThruJoin-15.mdp @@ -8,19 +8,18 @@ CREATE TABLE t2 (id smallint); EXPLAIN (costs off) SELECT * FROM t1 a JOIN t2 fa ON a.id = fa.id WHERE a.id <= 1; - QUERY PLAN - ------------------------------------------------------------------ + QUERY PLAN + ---------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) -> Hash Join - Hash Cond: (t1.id = (t2.id)::integer) - -> Seq Scan on t1 + Hash Cond: (a.id = (fa.id)::integer) + -> Seq Scan on t1 a Filter: (id <= 1) -> Hash - -> Redistribute Motion 3:3 (slice2; segments: 3) - Hash Key: (t2.id)::integer - -> Seq Scan on t2 - Filter: (id <= 1) + -> Seq Scan on t2 fa + Filter: (id <= 1) Optimizer: Pivotal Optimizer (GPORCA) + (9 rows) ]]> @@ -378,10 +377,10 @@ - + - + @@ -395,7 +394,7 @@ - + @@ -443,53 +442,34 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h b/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h index 5eb78fcd551c..05b3ea709725 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h +++ b/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h @@ -445,13 +445,22 @@ class CUtils static CExpressionArray *PdrgpexprDedup(CMemoryPool *mp, CExpressionArray *pdrgpexpr); + // common handler for direct expression comparison CUtils::Equals + // and distribution matching CUtils::EqualDistributions. + static BOOL Equals(const CExpression *pexprLeft, + const CExpression *pexprRight, bool fMatchDistribution); + // deep equality of expression trees static BOOL Equals(const CExpression *pexprLeft, const CExpression *pexprRight); + static BOOL EqualDistributions(const CExpression *pexprLeft, + const CExpression *pexprRight); + // compare expression against an array of expressions static BOOL FEqualAny(const CExpression *pexpr, - const CExpressionArray *pdrgpexpr); + const CExpressionArray *pdrgpexpr, + BOOL fMatchDistribution); // deep equality of expression arrays static BOOL Equals(const CExpressionArray *pdrgpexprLeft, @@ -459,7 +468,11 @@ class CUtils // check if first expression array contains all expressions in second array static BOOL Contains(const CExpressionArray *pdrgpexprFst, - const CExpressionArray *pdrgpexprSnd); + const CExpressionArray *pdrgpexprSnd, + bool fMatchDistribution); + + static BOOL ContainsDistributions(const CExpressionArray *pdrgpexprFst, + const CExpressionArray *pdrgpexprSnd); // return the number of occurrences of the given expression in the given // array of expressions @@ -1002,7 +1015,10 @@ class CUtils CExpression *join_expr); static BOOL Contains(const CExpressionArray *exprs, - CExpression *expr_to_match); + CExpression *expr_to_match, BOOL fMatchDistribution); + + static BOOL ContainsDistribution(const CExpressionArray *exprs, + CExpression *expr_to_match); static BOOL Equals(const CExpressionArrays *exprs_arr, const CExpressionArrays *other_exprs_arr); diff --git a/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp b/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp index 5dacb859179f..dcb4421aef0f 100644 --- a/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp +++ b/src/backend/gporca/libgpopt/src/base/CDistributionSpecHashed.cpp @@ -271,7 +271,7 @@ CDistributionSpecHashed::FMatchSubset( opfamily_other = (*pdsHashed->m_opfamilies)[ulInner]; } - if (CUtils::Equals(pexprOwn, pexprOther) && + if (CUtils::EqualDistributions(pexprOwn, pexprOther) && CUtils::Equals(opfamily_own, opfamily_other)) { fFound = true; @@ -298,7 +298,8 @@ CDistributionSpecHashed::FMatchSubset( GPOS_ASSERT(false == fFound); CExpressionArray *equiv_distribution_exprs = (*equiv_hash_exprs)[ulInner]; - if (CUtils::Contains(equiv_distribution_exprs, pexprOwn)) + if (CUtils::ContainsDistribution(equiv_distribution_exprs, + pexprOwn)) { fFound = true; break; @@ -520,12 +521,13 @@ CDistributionSpecHashed::FMatchHashedDistribution( equiv_distribution_exprs = (*all_equiv_exprs)[ul]; CExpression *pexprLeft = (*(pdshashed->m_pdrgpexpr))[ul]; CExpression *pexprRight = (*m_pdrgpexpr)[ul]; - BOOL fSuccess = CUtils::Equals(pexprLeft, pexprRight); + BOOL fSuccess = CUtils::EqualDistributions(pexprLeft, pexprRight); if (!fSuccess) { // if failed to find a equal match in the source distribution expr // array, check the equivalent exprs to find a match - fSuccess = CUtils::Contains(equiv_distribution_exprs, pexprRight); + fSuccess = CUtils::ContainsDistribution(equiv_distribution_exprs, + pexprRight); } if (!fSuccess) { @@ -619,6 +621,17 @@ CDistributionSpecHashed::Equals(const CDistributionSpec *input_spec) const CExpressionArrays *other_spec_equiv_exprs = other_spec->HashSpecEquivExprs(); + // The equality below should probably be strict, without need + // for calling CUtils::EqualDistributions, + // because the callers of this functions usually require + // direct expression matching. + // + // For example, one of the call chains leads to + // CCostContext::Equals and then to СGroupExpression::PccInsert + // + // But there are a lot of calls to this function, and given how + // nuanced ORCA can be, it is not strightforward to confirm this + // with certainty. return CUtils::Equals(spec_equiv_exprs, other_spec_equiv_exprs); } @@ -665,7 +678,8 @@ CDistributionSpecHashed::IsCoveredBy( const CDistributionSpecHashed *pds = this; while (pds && !covers) { - covers = CUtils::Contains(dist_cols_expr_array, pds->Pdrgpexpr()); + covers = CUtils::ContainsDistributions(dist_cols_expr_array, + pds->Pdrgpexpr()); pds = pds->PdshashedEquiv(); } return covers; @@ -756,13 +770,13 @@ CDistributionSpecHashed::ComputeEquivHashExprs( // if the predicate is a = b, and a is the current distribution expr, // then the equivalent expr is b CExpression *equiv_distribution_expr = NULL; - if (CUtils::Equals(left_distribution_expr, - distribution_expr)) + if (CUtils::EqualDistributions(left_distribution_expr, + distribution_expr)) { equiv_distribution_expr = right_distribution_expr; } - else if (CUtils::Equals(right_distribution_expr, - distribution_expr)) + else if (CUtils::EqualDistributions(right_distribution_expr, + distribution_expr)) { equiv_distribution_expr = left_distribution_expr; } diff --git a/src/backend/gporca/libgpopt/src/base/CUtils.cpp b/src/backend/gporca/libgpopt/src/base/CUtils.cpp index cc5fe5c4e319..bc115b60d8fb 100644 --- a/src/backend/gporca/libgpopt/src/base/CUtils.cpp +++ b/src/backend/gporca/libgpopt/src/base/CUtils.cpp @@ -1474,9 +1474,40 @@ CUtils::Equals(const CExpressionArray *pdrgpexprLeft, return fEqual; } -// deep equality of expression trees +// helper for CUtils::Equals, practically useless on its own +static const CExpression * +SkipCastWithinOpfamilyIfPossible(const CExpression *pexpr) +{ + if (pexpr->Pop()->Eopid() != COperator::EopScalarCast) + { + return pexpr; + } + + const CExpression *pexprChild = (*pexpr)[0]; + CScalar *popChild = CScalar::PopConvert(pexprChild->Pop()); + CScalarCast *popCast = CScalarCast::PopConvert(pexpr->Pop()); + IMDId *mdidSource = popChild->MdidType(); + IMDId *mdidDest = popCast->MdidType(); + + CMDAccessor *mda = COptCtxt::PoctxtFromTLS()->Pmda(); + const IMDType *mdtSourceType = mda->RetrieveType(mdidSource); + const IMDType *mdtDestType = mda->RetrieveType(mdidDest); + + IMDId *mdidSourceOpfamily = mdtSourceType->GetDistrOpfamilyMdid(); + IMDId *mdidTargetOpfamily = mdtDestType->GetDistrOpfamilyMdid(); + if (!CUtils::Equals(mdidSourceOpfamily, mdidTargetOpfamily)) + { + return pexpr; + } + + return pexprChild; +} + +// common handler for direct expression comparation CUtils::Equals +// and distribution matching CUtils::EqualDistributions BOOL -CUtils::Equals(const CExpression *pexprLeft, const CExpression *pexprRight) +CUtils::Equals(const CExpression *pexprLeft, const CExpression *pexprRight, + bool fMatchDistribution) { GPOS_CHECK_STACK_SIZE; @@ -1491,6 +1522,34 @@ CUtils::Equals(const CExpression *pexprLeft, const CExpression *pexprRight) { return true; } + // Allow an expression and the same expression + // casted to a different type within the same distribution opfamily + // be considered equal. + // E.g., let's suppose we have a table with a column of a type + // int2 named int2_column, then it would be considered equal + // to int2_column::int4 or int2_column::int8. + // This logic applies even if both expressions are converted + // to different types, e.g., int2_column::int4 = int2_column::int8 + // + // In this case, presence of the cast doesn't change how the value is + // distributed, meaning that redistribution motion is not required. + // + // Note that this check is not quite permissive, because distribution + // might not be changed even if there are multiple casts in a row, + // e.g. int2_column::int4::int8. + // But, as the time of writing this, the first cast will be turned into + // hard-to-detect coercion function anyway, so there is no need + // to perform this check recursively. + // + // Also, when using legacy hashfamilies, it is not guaranteed that + // different types within the same opfamily have the same hashfunction, + // so the logic above is not applicable. + if (GPOS_FTRACE(EopttraceConsiderOpfamiliesForDistribution) && + fMatchDistribution) + { + pexprLeft = SkipCastWithinOpfamilyIfPossible(pexprLeft); + pexprRight = SkipCastWithinOpfamilyIfPossible(pexprRight); + } // compare number of children and root operators if (pexprLeft->Arity() != pexprRight->Arity() || @@ -1507,6 +1566,20 @@ CUtils::Equals(const CExpression *pexprLeft, const CExpression *pexprRight) return FMatchChildrenUnordered(pexprLeft, pexprRight); } +// deep equality of expression trees +BOOL +CUtils::Equals(const CExpression *pexprLeft, const CExpression *pexprRight) +{ + return Equals(pexprLeft, pexprRight, false); +} + +BOOL +CUtils::EqualDistributions(const CExpression *pexprLeft, + const CExpression *pexprRight) +{ + return Equals(pexprLeft, pexprRight, true); +} + // check if two expressions have the same children in any order BOOL CUtils::FMatchChildrenUnordered(const CExpression *pexprLeft, @@ -1565,7 +1638,8 @@ CUtils::UlOccurrences(const CExpression *pexpr, CExpressionArray *pdrgpexpr) // compare expression against an array of expressions BOOL -CUtils::FEqualAny(const CExpression *pexpr, const CExpressionArray *pdrgpexpr) +CUtils::FEqualAny(const CExpression *pexpr, const CExpressionArray *pdrgpexpr, + BOOL fMatchDistribution) { GPOS_ASSERT(NULL != pexpr); @@ -1573,7 +1647,7 @@ CUtils::FEqualAny(const CExpression *pexpr, const CExpressionArray *pdrgpexpr) BOOL fEqual = false; for (ULONG ul = 0; !fEqual && ul < size; ul++) { - fEqual = Equals(pexpr, (*pdrgpexpr)[ul]); + fEqual = Equals(pexpr, (*pdrgpexpr)[ul], fMatchDistribution); } return fEqual; @@ -1582,7 +1656,7 @@ CUtils::FEqualAny(const CExpression *pexpr, const CExpressionArray *pdrgpexpr) // check if first expression array contains all expressions in second array BOOL CUtils::Contains(const CExpressionArray *pdrgpexprFst, - const CExpressionArray *pdrgpexprSnd) + const CExpressionArray *pdrgpexprSnd, BOOL fMatchDistribution) { GPOS_ASSERT(NULL != pdrgpexprFst); GPOS_ASSERT(NULL != pdrgpexprSnd); @@ -1601,12 +1675,20 @@ CUtils::Contains(const CExpressionArray *pdrgpexprFst, BOOL fContains = true; for (ULONG ul = 0; fContains && ul < size; ul++) { - fContains = FEqualAny((*pdrgpexprSnd)[ul], pdrgpexprFst); + fContains = + FEqualAny((*pdrgpexprSnd)[ul], pdrgpexprFst, fMatchDistribution); } return fContains; } +BOOL +CUtils::ContainsDistributions(const CExpressionArray *pdrgpexprFst, + const CExpressionArray *pdrgpexprSnd) +{ + return CUtils::Contains(pdrgpexprFst, pdrgpexprSnd, true); +} + // generate a Not expression on top of the given expression CExpression * CUtils::PexprNegate(CMemoryPool *mp, CExpression *pexpr) @@ -4907,33 +4989,53 @@ CUtils::PexprMatchEqualityOrINDF( CScalar::PopConvert(pexprPredOuter->Pop())->MdidType(); IMDId *pmdidTypeInner = CScalar::PopConvert(pexprPredInner->Pop())->MdidType(); - if (!pmdidTypeOuter->Equals(pmdidTypeInner)) + + CMDAccessor *mdAccessor = COptCtxt::PoctxtFromTLS()->Pmda(); + + IMDId *mdidOpfamilyInner = + mdAccessor->RetrieveType(pmdidTypeInner)->GetDistrOpfamilyMdid(); + + IMDId *mdidOpfamilyOuter = + mdAccessor->RetrieveType(pmdidTypeOuter)->GetDistrOpfamilyMdid(); + + // Callers of this function expect that they can distribute by the + // expression returned, and there were no guarantees + // about it before this call. + // + // For the time being, just don't return such expressions, + // but it doesn't seem that this logic belongs here. + if (GPOS_FTRACE(EopttraceConsiderOpfamiliesForDistribution) && + (!mdidOpfamilyInner || !mdidOpfamilyOuter)) { - // only consider equality of identical types continue; } + // Note that we need to manually remove binary coercible casts here, + // while for other join types this is done when they are being constructed + // (see CPhysicalJoin::AlignJoinKeyOuterInner). + CExpression *pexprOuterToMatch = + CCastUtils::PexprWithoutBinaryCoercibleCasts(pexprPredOuter); + CExpression *pexprInnerToMatch = + CCastUtils::PexprWithoutBinaryCoercibleCasts(pexprPredInner); pexprToMatch = CCastUtils::PexprWithoutBinaryCoercibleCasts(pexprToMatch); - if (CUtils::Equals( - CCastUtils::PexprWithoutBinaryCoercibleCasts(pexprPredOuter), - pexprToMatch)) + + if (CUtils::EqualDistributions(pexprOuterToMatch, pexprToMatch)) { pexprMatching = pexprPredInner; - break; } - - if (CUtils::Equals( - CCastUtils::PexprWithoutBinaryCoercibleCasts(pexprPredInner), - pexprToMatch)) + if (CUtils::EqualDistributions(pexprInnerToMatch, pexprToMatch)) { pexprMatching = pexprPredOuter; + } + if (pexprMatching) + { + pexprMatching = + CCastUtils::PexprWithoutBinaryCoercibleCasts(pexprMatching); break; } } - if (NULL != pexprMatching) - return CCastUtils::PexprWithoutBinaryCoercibleCasts(pexprMatching); return pexprMatching; } @@ -4966,7 +5068,8 @@ CUtils::MakeJoinWithoutInferredPreds(CMemoryPool *mp, CExpression *join_expr) // check if the input expr array contains the expr BOOL -CUtils::Contains(const CExpressionArray *exprs, CExpression *expr_to_match) +CUtils::Contains(const CExpressionArray *exprs, CExpression *expr_to_match, + BOOL fMatchDistribution) { if (NULL == exprs) { @@ -4977,11 +5080,18 @@ CUtils::Contains(const CExpressionArray *exprs, CExpression *expr_to_match) for (ULONG ul = 0; ul < exprs->Size() && !contains; ul++) { CExpression *expr = (*exprs)[ul]; - contains = CUtils::Equals(expr, expr_to_match); + contains = CUtils::Equals(expr, expr_to_match, fMatchDistribution); } return contains; } +BOOL +CUtils::ContainsDistribution(const CExpressionArray *exprs, + CExpression *expr_to_match) +{ + return Contains(exprs, expr_to_match, true); +} + BOOL CUtils::Equals(const CExpressionArrays *exprs_arr, const CExpressionArrays *other_exprs_arr) diff --git a/src/backend/gporca/libgpopt/src/operators/CPhysicalHashJoin.cpp b/src/backend/gporca/libgpopt/src/operators/CPhysicalHashJoin.cpp index fc7850fdae12..790375d781c6 100644 --- a/src/backend/gporca/libgpopt/src/operators/CPhysicalHashJoin.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CPhysicalHashJoin.cpp @@ -391,13 +391,13 @@ CPhysicalHashJoin::PdshashedMatching( if (GPOPT_INVALID_OPT_REQUEST != ulOptReq && ulSourceSize > ulOptReq) { CExpression *source_expr = (*pdrgpexprSource)[ulOptReq]; - BOOL fSuccess = CUtils::Equals(pexprDlvrd, source_expr); + BOOL fSuccess = CUtils::EqualDistributions(pexprDlvrd, source_expr); if (!fSuccess) { // if failed to find a equal match in the source distribution expr // array, check the equivalent exprs to find a match - fSuccess = - CUtils::Contains(equiv_distribution_exprs, source_expr); + fSuccess = CUtils::ContainsDistribution( + equiv_distribution_exprs, source_expr); } if (fSuccess) { @@ -427,13 +427,13 @@ CPhysicalHashJoin::PdshashedMatching( BOOL fSuccess = false; CExpression *source_expr = (*pdrgpexprSource)[idx]; - fSuccess = CUtils::Equals(pexprDlvrd, source_expr); + fSuccess = CUtils::EqualDistributions(pexprDlvrd, source_expr); if (!fSuccess) { // if failed to find a equal match in the source distribution expr // array, check the equivalent exprs to find a match - fSuccess = - CUtils::Contains(equiv_distribution_exprs, source_expr); + fSuccess = CUtils::ContainsDistribution( + equiv_distribution_exprs, source_expr); } if (fSuccess) { diff --git a/src/backend/gporca/libgpopt/src/operators/CPhysicalJoin.cpp b/src/backend/gporca/libgpopt/src/operators/CPhysicalJoin.cpp index 00cb7cc4c205..f9d58db0e805 100644 --- a/src/backend/gporca/libgpopt/src/operators/CPhysicalJoin.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CPhysicalJoin.cpp @@ -23,6 +23,7 @@ #include "gpopt/operators/CScalarCmp.h" #include "gpopt/operators/CScalarIsDistinctFrom.h" #include "naucrates/md/IMDScalarOp.h" +#include "naucrates/traceflags/traceflags.h" using namespace gpopt; @@ -401,6 +402,23 @@ CPhysicalJoin::PedInnerHashedFromOuterHashed( mdAccessor->RetrieveType(pmdidTypeOuter) ->GetDistrOpfamilyMdid(); + // We may get here with null hashfamilies if we join on integer types + // (or other 'non-generic' types) + // with EopttraceConsiderOpfamiliesForDistribution is set to off. + // This is not the common case, because this flag is on by default. + // + // If a type has no dedicated subclass (i.e., it is CMDTypeGenericGPDB), + // hashfamilies are non-null most of the time, since they are set + // regardless of the flag. + // But even in such case the distribution can be missing if + // a type doesn't have one in the first place. + // For example, a type can be created this way by a user. + if (!mdidOpfamilyInner || !mdidOpfamilyOuter) + { + fSuccess = false; + continue; + } + if (mdidOpfamilyOuter->Equals(mdidOpfamilyInner) && mdAccessor->RetrieveType(pmdidTypeInner)->IsHashable()) { diff --git a/src/test/regress/expected/gp_cast.out b/src/test/regress/expected/gp_cast.out new file mode 100644 index 000000000000..bc91dd334202 --- /dev/null +++ b/src/test/regress/expected/gp_cast.out @@ -0,0 +1,1207 @@ +-- This file contains tests related to the casting behavior, primarily in joins. +-- Some of the tests performed here may already be present inside other files, +-- but it is nice to have a centralized place to observe and compare the behavior of the planners. +-- Tables here are prefixed with cst (which stands for 'cast') to avoid collisions. +-- One of the tests below fails with an assertion, that prints its location in the source code. +-- Make sure that we don't need to update this file when its line changes. +-- start_matchsubs +-- m/DETAIL: CTranslatorQueryToDXL.cpp:\d+: Failed assertion/ +-- s/DETAIL: CTranslatorQueryToDXL.cpp:\d+: Failed assertion/DETAIL: CTranslatorQueryToDXL.cpp:###: Failed assertion/ +-- end_matchsubs +set optimizer_enable_mergejoin = false; +-- We want to specifically test hashjoins for several queries below. +-- Nested loop joins are not constructed for these queries at the time, +-- and probably never will be, but let's make them additionally unlikely just in case. +set optimizer_nestloop_factor = 102400; +set enable_mergejoin = false; +set enable_nestloop = false; +set optimizer_trace_fallback = true; +create table cst_int2 as (select a::int2 from generate_series(0, 10) as a) distributed by (a); +create table cst_int4 as (select a::int4 from generate_series(5, 15) as a) distributed by (a); +create table cst_int8 as (select a::int8 from generate_series(10, 20) as a) distributed by (a); +create table cst_float4 as (select a::float4 from generate_series(5, 15, 1) as a) distributed by (a); +create table cst_float8 as (select a::float8 from generate_series(5, 15, 1) as a) distributed by (a); +create table cst_text as (select a::text from generate_series(5, 15) as a) distributed by (a); +create table cst_varchar as (select a::varchar from generate_series(5, 15) as a) distributed by (a); +create table cst_int2_int4 as (select gen::int2 as a, gen::int4 as b from generate_series(1, 10) as gen) distributed by (a, b); +create table cst_int4_int8 as (select gen::int4 as a, gen::int8 as b from generate_series(1, 10) as gen) distributed by (a, b); +-- Perform an inner join on all hash opfamilies containing more than one type. +-- We shouldn't see any redistributions, because: +-- The postgres-based planner has operators that work on different types. +-- ORCA doesn't support such operations, but it knows when a cast doesn't change a distribution. +set optimizer_join_order = query; +explain (verbose, costs off) select * from cst_int2 join cst_int4 using(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Join + Output: cst_int4.a + Hash Cond: (cst_int2.a = cst_int4.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 join cst_int4 using(a); + a +---- + 7 + 8 + 5 + 6 + 9 + 10 +(6 rows) + +explain (verbose, costs off) select * from cst_int2 join cst_int8 using(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: (cst_int2.a = cst_int8.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 join cst_int8 using(a); + a +---- + 10 +(1 row) + +explain (verbose, costs off) select * from cst_int4 join cst_int8 using(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: (cst_int4.a = cst_int8.a) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int4 join cst_int8 using(a); + a +---- + 12 + 15 + 10 + 11 + 13 + 14 +(6 rows) + +explain (verbose, costs off) select * from cst_float4 join cst_float8 using(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_float8.a + -> Hash Join + Output: cst_float8.a + Hash Cond: (cst_float4.a = cst_float8.a) + -> Seq Scan on public.cst_float4 + Output: cst_float4.a + -> Hash + Output: cst_float8.a + -> Seq Scan on public.cst_float8 + Output: cst_float8.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_float4 join cst_float8 using(a); + a +---- + 6 + 7 + 8 + 9 + 11 + 12 + 13 + 14 + 15 + 10 + 5 +(11 rows) + +-- Same thing with the sides swapped +explain (verbose, costs off) select * from cst_int4 join cst_int2 using(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = cst_int2.a) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int4 join cst_int2 using(a); + a +---- + 7 + 8 + 5 + 6 + 9 + 10 +(6 rows) + +explain (verbose, costs off) select * from cst_int8 join cst_int2 using(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: (cst_int8.a = cst_int2.a) + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int8 join cst_int2 using(a); + a +---- + 10 +(1 row) + +explain (verbose, costs off) select * from cst_int8 join cst_int4 using(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: (cst_int8.a = cst_int4.a) + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int8 join cst_int4 using(a); + a +---- + 10 + 11 + 13 + 14 + 12 + 15 +(6 rows) + +explain (verbose, costs off) select * from cst_float8 join cst_float4 using(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_float8.a + -> Hash Join + Output: cst_float8.a + Hash Cond: (cst_float8.a = cst_float4.a) + -> Seq Scan on public.cst_float8 + Output: cst_float8.a + -> Hash + Output: cst_float4.a + -> Seq Scan on public.cst_float4 + Output: cst_float4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_float8 join cst_float4 using(a); + a +---- + 5 + 10 + 6 + 7 + 8 + 9 + 11 + 12 + 13 + 14 + 15 +(11 rows) + +-- Confirm that casting logic works recursively +explain (verbose, costs off) +select * from cst_int2 + join cst_int8 using(a) + join cst_int4 using(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: (cst_int2.a = cst_int4.a) + -> Hash Join + Output: cst_int2.a, cst_int8.a + Hash Cond: (cst_int2.a = cst_int8.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(20 rows) + +select * from cst_int2 + join cst_int8 using(a) + join cst_int4 using(a); + a +---- + 10 +(1 row) + +reset optimizer_join_order; +-- BUG: this test is failing for ORCA. +-- It's the test that requires start_matchsubs. +explain (verbose, costs off) select * from cst_int2 left join cst_int4 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: ((cst_int2.a)::integer) + -> Hash Left Join + Output: cst_int2.a + Hash Cond: (cst_int2.a = cst_int4.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 left join cst_int4 using(a); + a +---- + 2 + 3 + 4 + 7 + 8 + 5 + 6 + 9 + 10 + 0 + 1 +(11 rows) + +explain (verbose, costs off) select * from cst_int2 right join cst_int4 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Left Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = cst_int2.a) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 right join cst_int4 using(a); + a +---- + 12 + 15 + 5 + 6 + 9 + 10 + 11 + 13 + 14 + 7 + 8 +(11 rows) + +explain (verbose, costs off) +select * from cst_int4 +where exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Semi Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = cst_int2.a) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int4 +where exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + a +---- + 5 + 6 + 9 + 10 + 7 + 8 +(6 rows) + +explain (verbose, costs off) +select * from cst_int4 +where not exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Anti Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = cst_int2.a) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int4 +where not exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + a +---- + 12 + 15 + 11 + 13 + 14 +(5 rows) + +explain (verbose, costs off) +select * from cst_int4 +where cst_int4.a not in (select * from cst_int2); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_int4.a + -> Hash Left Anti Semi (Not-In) Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = cst_int2.a) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(15 rows) + +select * from cst_int4 +where cst_int4.a not in (select * from cst_int2); + a +---- + 11 + 13 + 14 + 12 + 15 +(5 rows) + +explain (verbose, costs off) +select * from cst_int2 natural join cst_int4; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Join + Output: cst_int4.a + Hash Cond: (cst_int2.a = cst_int4.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 natural join cst_int4; + a +---- + 5 + 6 + 9 + 10 + 7 + 8 +(6 rows) + +-- Here, instead of an implicit cast, an explicit one is present. +-- The postgres-based planner should require a redistribution, because +-- distribution of the cst_int2 table is not directly equal to the left-hand side of the expression. +-- ORCA, on the other hand, can see that redistribution is unnecessary in this case. +explain (verbose, costs off) select * from cst_int2 join cst_int4 on cst_int2.a::int4 = cst_int4.a; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_int2.a, cst_int4.a + -> Hash Join + Output: cst_int2.a, cst_int4.a + Hash Cond: ((cst_int2.a)::integer = cst_int4.a) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: cst_int2.a + Hash Key: (cst_int2.a)::integer + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_int2 join cst_int4 on cst_int2.a::int4 = cst_int4.a; + a | a +----+---- + 7 | 7 + 8 | 8 + 5 | 5 + 6 | 6 + 9 | 9 + 10 | 10 +(6 rows) + +-- The same thing, but with multiple casts in a row. +-- Because the first cast tends to be converted directly to a conversion function, +-- ORCA shouldn't be able to detect the first coercion and should require a redistribution. +explain (verbose, costs off) select * from cst_float4 as cst_float4_f join cst_float4 as cst_float4_s on cst_float4_f.a::int::float4 = cst_float4_s.a; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_float4_f.a, cst_float4_s.a + -> Hash Join + Output: cst_float4_f.a, cst_float4_s.a + Hash Cond: (((cst_float4_f.a)::integer)::real = cst_float4_s.a) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: cst_float4_f.a + Hash Key: ((cst_float4_f.a)::integer)::real + -> Seq Scan on public.cst_float4 cst_float4_f + Output: cst_float4_f.a + -> Hash + Output: cst_float4_s.a + -> Seq Scan on public.cst_float4 cst_float4_s + Output: cst_float4_s.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_float4 as cst_float4_f join cst_float4 as cst_float4_s on cst_float4_f.a::int::float4 = cst_float4_s.a; + a | a +----+---- + 10 | 10 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 + 9 | 9 + 11 | 11 + 12 | 12 + 13 | 13 + 14 | 14 + 15 | 15 +(11 rows) + +-- Check that binary coercible casts are ruled out +explain (verbose, costs off) select * from cst_varchar as foo join cst_varchar as bar using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: foo.a + -> Hash Join + Output: foo.a + Hash Cond: ((foo.a)::text = (bar.a)::text) + -> Seq Scan on public.cst_varchar foo + Output: foo.a + -> Hash + Output: bar.a + -> Seq Scan on public.cst_varchar bar + Output: bar.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_varchar as foo join cst_varchar as bar using(a); + a +---- + 7 + 5 + 6 + 8 + 15 + 9 + 10 + 11 + 12 + 13 + 14 +(11 rows) + +explain (verbose, costs off) select * from cst_varchar join cst_text using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_varchar.a + -> Hash Join + Output: cst_varchar.a + Hash Cond: ((cst_varchar.a)::text = cst_text.a) + -> Seq Scan on public.cst_varchar + Output: cst_varchar.a + -> Hash + Output: cst_text.a + -> Seq Scan on public.cst_text + Output: cst_text.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_varchar join cst_text using(a); + a +---- + 5 + 6 + 8 + 15 + 7 + 9 + 10 + 11 + 12 + 13 + 14 +(11 rows) + +-- Сheck that we don't rule out necessary distributions. +-- Most basic cases: +explain (verbose, costs off) select * from cst_float4 join cst_int4 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_float4.a + -> Hash Join + Output: cst_float4.a + Hash Cond: ((cst_int4.a)::double precision = cst_float4.a) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: cst_int4.a + Hash Key: (cst_int4.a)::double precision + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_float4.a + -> Seq Scan on public.cst_float4 + Output: cst_float4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_float4 join cst_int4 using(a); + a +---- + 10 + 5 + 12 + 15 + 7 + 8 + 6 + 9 + 11 + 13 + 14 +(11 rows) + +explain (verbose, costs off) select * from cst_int4 join cst_text on cst_int4.a = cst_text.a::int4; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_int4.a, cst_text.a + -> Hash Join + Output: cst_int4.a, cst_text.a + Hash Cond: ((cst_text.a)::integer = cst_int4.a) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: cst_text.a + Hash Key: (cst_text.a)::integer + -> Seq Scan on public.cst_text + Output: cst_text.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_int4 join cst_text on cst_int4.a = cst_text.a::int4; + a | a +----+---- + 8 | 8 + 7 | 7 + 15 | 15 + 12 | 12 + 5 | 5 + 6 | 6 + 9 | 9 + 10 | 10 + 11 | 11 + 13 | 13 + 14 | 14 +(11 rows) + +-- ORCA: in order for there queries to work, equivalent expressions should be matched correctly +explain (verbose, costs off) +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (int8_cte as cte_2 join cst_int4 using(a)) using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: (cst_int2.a = cst_int4.a) + -> Hash Join + Output: cst_int2.a, cst_int8.a, cst_int8_1.a + Hash Cond: (cst_int2.a = cst_int8_1.a) + -> Hash Join + Output: cst_int2.a, cst_int8.a + Hash Cond: (cst_int2.a = cst_int8.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Hash + Output: cst_int8_1.a + -> Seq Scan on public.cst_int8 cst_int8_1 + Output: cst_int8_1.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(27 rows) + +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (int8_cte as cte_2 join cst_int4 using(a)) using(a); + a +---- + 10 +(1 row) + +explain (verbose, costs off) +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (cst_int4 join int8_cte as cte_2 using(a)) using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: (cst_int2.a = cst_int8_1.a) + -> Hash Join + Output: cst_int2.a, cst_int8.a, cst_int4.a + Hash Cond: (cst_int2.a = cst_int4.a) + -> Hash Join + Output: cst_int2.a, cst_int8.a + Hash Cond: (cst_int2.a = cst_int8.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int8_1.a + -> Seq Scan on public.cst_int8 cst_int8_1 + Output: cst_int8_1.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(27 rows) + +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (cst_int4 join int8_cte as cte_2 using(a)) using(a); + a +---- + 10 +(1 row) + +-- Test distribution by multiple keys +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice1; segments: 3) + Output: t1.a, t1.b, t2.a, t2.b + -> Hash Join + Output: t1.a, t1.b, t2.a, t2.b + Hash Cond: ((t1.a = t2.a) AND (t1.b = t2.b)) + -> Seq Scan on public.cst_int2_int4 t1 + Output: t1.a, t1.b + -> Hash + Output: t2.a, t2.b + -> Seq Scan on public.cst_int4_int8 t2 + Output: t2.a, t2.b + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); + a | b | a | b +----+----+----+---- + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 4 | 4 | 4 + 5 | 5 | 5 | 5 + 7 | 7 | 7 | 7 + 9 | 9 | 9 | 9 + 10 | 10 | 10 | 10 + 1 | 1 | 1 | 1 + 6 | 6 | 6 | 6 + 8 | 8 | 8 | 8 +(10 rows) + +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.b and t1.b = t2.a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice2; segments: 3) + Output: t1.a, t1.b, t2.a, t2.b + -> Hash Join + Output: t1.a, t1.b, t2.a, t2.b + Hash Cond: ((t1.a = t2.b) AND (t1.b = t2.a)) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: t1.a, t1.b + Hash Key: t1.b, t1.a + -> Seq Scan on public.cst_int2_int4 t1 + Output: t1.a, t1.b + -> Hash + Output: t2.a, t2.b + -> Seq Scan on public.cst_int4_int8 t2 + Output: t2.a, t2.b + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.b and t1.b = t2.a); + a | b | a | b +----+----+----+---- + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 4 | 4 | 4 + 5 | 5 | 5 | 5 + 7 | 7 | 7 | 7 + 9 | 9 | 9 | 9 + 10 | 10 | 10 | 10 + 1 | 1 | 1 | 1 + 6 | 6 | 6 | 6 + 8 | 8 | 8 | 8 +(10 rows) + +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int2 as t2 on (t1.a = t2.a); + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice2; segments: 3) + Output: t1.a, t1.b, t2.a + -> Hash Join + Output: t1.a, t1.b, t2.a + Hash Cond: (t1.a = t2.a) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: t1.a, t1.b + Hash Key: t1.a + -> Seq Scan on public.cst_int2_int4 t1 + Output: t1.a, t1.b + -> Hash + Output: t2.a + -> Seq Scan on public.cst_int2 t2 + Output: t2.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_int2_int4 as t1 join cst_int2 as t2 on (t1.a = t2.a); + a | b | a +----+----+---- + 2 | 2 | 2 + 3 | 3 | 3 + 4 | 4 | 4 + 7 | 7 | 7 + 8 | 8 | 8 + 1 | 1 | 1 + 9 | 9 | 9 + 10 | 10 | 10 + 5 | 5 | 5 + 6 | 6 | 6 +(10 rows) + +set optimizer_join_order = query; +explain (verbose, costs off) +with cst_int2_int4_copy as (select * from cst_int2_int4) +select * from cst_int2_int4 + natural join cst_int4_int8 + natural join cst_int2_int4_copy; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4_int8.a, cst_int4_int8.b + -> Hash Join + Output: cst_int4_int8.a, cst_int4_int8.b + Hash Cond: ((cst_int4_int8.a = cst_int2_int4.a) AND (cst_int4_int8.b = cst_int2_int4.b)) + -> Seq Scan on public.cst_int4_int8 + Output: cst_int4_int8.a, cst_int4_int8.b + -> Hash + Output: cst_int2_int4.a, cst_int2_int4.b, cst_int2_int4_1.a, cst_int2_int4_1.b + -> Hash Join + Output: cst_int2_int4.a, cst_int2_int4.b, cst_int2_int4_1.a, cst_int2_int4_1.b + Hash Cond: ((cst_int2_int4.a = cst_int2_int4_1.a) AND (cst_int2_int4.b = cst_int2_int4_1.b)) + -> Seq Scan on public.cst_int2_int4 + Output: cst_int2_int4.a, cst_int2_int4.b + -> Hash + Output: cst_int2_int4_1.a, cst_int2_int4_1.b + -> Seq Scan on public.cst_int2_int4 cst_int2_int4_1 + Output: cst_int2_int4_1.a, cst_int2_int4_1.b + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(20 rows) + +with cst_int2_int4_copy as (select * from cst_int2_int4) +select * from cst_int2_int4 + natural join cst_int4_int8 + natural join cst_int2_int4_copy; + a | b +----+---- + 9 | 9 + 10 | 10 + 1 | 1 + 6 | 6 + 8 | 8 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 7 | 7 +(10 rows) + +-- Test several queries with nested-loop join +reset optimizer_nestloop_factor; +set enable_nestloop = true; +set optimizer_enable_hashjoin = false; +set enable_hashjoin = false; +explain (verbose, costs off) select * from cst_int2 join cst_int4 using(a); + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Nested Loop + Output: cst_int4.a + Join Filter: (cst_int2.a = cst_int4.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Materialize + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Postgres query optimizer + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on, optimizer=off, optimizer_join_order=query +(13 rows) + +select * from cst_int2 join cst_int4 using(a); + a +---- + 5 + 6 + 9 + 10 + 7 + 8 +(6 rows) + +-- Subtle behavior ahead: in ORCA, nested loop joins don't support equivalent expressions +-- like hash joins do. +-- So, when join order is fixed, we need to be careful with the join condition, +-- otherwise we might get a broadcast motion. +explain (verbose, costs off) +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int2.a, cst_int4.a, cst_int8.a + -> Nested Loop + Output: cst_int2.a, cst_int4.a, cst_int8.a + Join Filter: (cst_int2.a = cst_int8.a) + -> Nested Loop + Output: cst_int2.a, cst_int4.a + Join Filter: (cst_int2.a = cst_int4.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Materialize + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Materialize + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + Optimizer: Postgres query optimizer + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on, optimizer=off, optimizer_join_order=query +(20 rows) + +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + a | a | a +----+----+---- + 10 | 10 | 10 +(1 row) + +-- Luckily, when join order is not fixed, optimizer finds a join path that +-- gets rid of the broadcast. +reset optimizer_join_order; +explain (verbose, costs off) +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + QUERY PLAN +---------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int2.a, cst_int4.a, cst_int8.a + -> Nested Loop + Output: cst_int2.a, cst_int4.a, cst_int8.a + Join Filter: (cst_int2.a = cst_int8.a) + -> Nested Loop + Output: cst_int2.a, cst_int4.a + Join Filter: (cst_int2.a = cst_int4.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Materialize + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Materialize + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + Optimizer: Postgres query optimizer + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on, optimizer=off +(20 rows) + +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + a | a | a +----+----+---- + 10 | 10 | 10 +(1 row) + +-- Test very specific logic for a join on several keys with an index in ORCA +create index cst_int4_int8_idx on cst_int4_int8 (b); +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); + QUERY PLAN +---------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: t1.a, t1.b, t2.a, t2.b + -> Nested Loop + Output: t1.a, t1.b, t2.a, t2.b + Join Filter: ((t1.a = t2.a) AND (t1.b = t2.b)) + -> Seq Scan on public.cst_int2_int4 t1 + Output: t1.a, t1.b + -> Materialize + Output: t2.a, t2.b + -> Seq Scan on public.cst_int4_int8 t2 + Output: t2.a, t2.b + Optimizer: Postgres query optimizer + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on, optimizer=off +(13 rows) + +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); + a | b | a | b +----+----+----+---- + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 4 | 4 | 4 + 5 | 5 | 5 | 5 + 7 | 7 | 7 | 7 + 1 | 1 | 1 | 1 + 6 | 6 | 6 | 6 + 8 | 8 | 8 | 8 + 9 | 9 | 9 | 9 + 10 | 10 | 10 | 10 +(10 rows) + +drop index cst_int4_int8_idx; +-- Binary coercible casts should be ruled out too +explain (verbose, costs off) select * from cst_varchar as foo join cst_varchar as bar using(a); + QUERY PLAN +---------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: foo.a + -> Nested Loop + Output: foo.a + Join Filter: ((foo.a)::text = (bar.a)::text) + -> Seq Scan on public.cst_varchar foo + Output: foo.a + -> Materialize + Output: bar.a + -> Seq Scan on public.cst_varchar bar + Output: bar.a + Optimizer: Postgres query optimizer + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on, optimizer=off +(13 rows) + +select * from cst_varchar as foo join cst_varchar as bar using(a); + a +---- + 9 + 10 + 11 + 12 + 13 + 14 + 5 + 6 + 8 + 15 + 7 +(11 rows) + +explain (verbose, costs off) select * from cst_varchar join cst_text using(a); + QUERY PLAN +---------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_varchar.a + -> Nested Loop + Output: cst_varchar.a + Join Filter: ((cst_varchar.a)::text = cst_text.a) + -> Seq Scan on public.cst_varchar + Output: cst_varchar.a + -> Materialize + Output: cst_text.a + -> Seq Scan on public.cst_text + Output: cst_text.a + Optimizer: Postgres query optimizer + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on, optimizer=off +(13 rows) + +select * from cst_varchar join cst_text using(a); + a +---- + 7 + 9 + 10 + 11 + 12 + 13 + 14 + 5 + 6 + 8 + 15 +(11 rows) + +drop table cst_int2; +drop table cst_int4; +drop table cst_int8; +drop table cst_float4; +drop table cst_float8; +drop table cst_text; +drop table cst_varchar; +drop table cst_int2_int4; +drop table cst_int4_int8; +reset optimizer_trace_fallback; +reset enable_hashjoin; +reset enable_nestloop; +reset enable_mergejoin; +reset optimizer_enable_hashjoin; +reset optimizer_nestloop_factor; +reset optimizer_enable_mergejoin; diff --git a/src/test/regress/expected/gp_cast_optimizer.out b/src/test/regress/expected/gp_cast_optimizer.out new file mode 100644 index 000000000000..996f74215444 --- /dev/null +++ b/src/test/regress/expected/gp_cast_optimizer.out @@ -0,0 +1,1215 @@ +-- This file contains tests related to the casting behavior, primarily in joins. +-- Some of the tests performed here may already be present inside other files, +-- but it is nice to have a centralized place to observe and compare the behavior of the planners. +-- Tables here are prefixed with cst (which stands for 'cast') to avoid collisions. +-- One of the tests below fails with an assertion, that prints its location in the source code. +-- Make sure that we don't need to update this file when its line changes. +-- start_matchsubs +-- m/DETAIL: CTranslatorQueryToDXL.cpp:\d+: Failed assertion/ +-- s/DETAIL: CTranslatorQueryToDXL.cpp:\d+: Failed assertion/DETAIL: CTranslatorQueryToDXL.cpp:###: Failed assertion/ +-- end_matchsubs +set optimizer_enable_mergejoin = false; +-- We want to specifically test hashjoins for several queries below. +-- Nested loop joins are not constructed for these queries at the time, +-- and probably never will be, but let's make them additionally unlikely just in case. +set optimizer_nestloop_factor = 102400; +set enable_mergejoin = false; +set enable_nestloop = false; +set optimizer_trace_fallback = true; +create table cst_int2 as (select a::int2 from generate_series(0, 10) as a) distributed by (a); +create table cst_int4 as (select a::int4 from generate_series(5, 15) as a) distributed by (a); +create table cst_int8 as (select a::int8 from generate_series(10, 20) as a) distributed by (a); +create table cst_float4 as (select a::float4 from generate_series(5, 15, 1) as a) distributed by (a); +create table cst_float8 as (select a::float8 from generate_series(5, 15, 1) as a) distributed by (a); +create table cst_text as (select a::text from generate_series(5, 15) as a) distributed by (a); +create table cst_varchar as (select a::varchar from generate_series(5, 15) as a) distributed by (a); +create table cst_int2_int4 as (select gen::int2 as a, gen::int4 as b from generate_series(1, 10) as gen) distributed by (a, b); +create table cst_int4_int8 as (select gen::int4 as a, gen::int8 as b from generate_series(1, 10) as gen) distributed by (a, b); +-- Perform an inner join on all hash opfamilies containing more than one type. +-- We shouldn't see any redistributions, because: +-- The postgres-based planner has operators that work on different types. +-- ORCA doesn't support such operations, but it knows when a cast doesn't change a distribution. +set optimizer_join_order = query; +explain (verbose, costs off) select * from cst_int2 join cst_int4 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Join + Output: cst_int4.a + Hash Cond: ((cst_int2.a)::integer = cst_int4.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 join cst_int4 using(a); + a +---- + 7 + 8 + 5 + 6 + 9 + 10 +(6 rows) + +explain (verbose, costs off) select * from cst_int2 join cst_int8 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: ((cst_int2.a)::bigint = cst_int8.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 join cst_int8 using(a); + a +---- + 10 +(1 row) + +explain (verbose, costs off) select * from cst_int4 join cst_int8 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: ((cst_int4.a)::bigint = cst_int8.a) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int4 join cst_int8 using(a); + a +---- + 10 + 11 + 13 + 14 + 12 + 15 +(6 rows) + +explain (verbose, costs off) select * from cst_float4 join cst_float8 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_float8.a + -> Hash Join + Output: cst_float8.a + Hash Cond: ((cst_float4.a)::double precision = cst_float8.a) + -> Seq Scan on public.cst_float4 + Output: cst_float4.a + -> Hash + Output: cst_float8.a + -> Seq Scan on public.cst_float8 + Output: cst_float8.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_float4 join cst_float8 using(a); + a +---- + 5 + 10 + 6 + 7 + 8 + 9 + 11 + 12 + 13 + 14 + 15 +(11 rows) + +-- Same thing with the sides swapped +explain (verbose, costs off) select * from cst_int4 join cst_int2 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = (cst_int2.a)::integer) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int4 join cst_int2 using(a); + a +---- + 5 + 6 + 9 + 10 + 7 + 8 +(6 rows) + +explain (verbose, costs off) select * from cst_int8 join cst_int2 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: (cst_int8.a = (cst_int2.a)::bigint) + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int8 join cst_int2 using(a); + a +---- + 10 +(1 row) + +explain (verbose, costs off) select * from cst_int8 join cst_int4 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: (cst_int8.a = (cst_int4.a)::bigint) + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int8 join cst_int4 using(a); + a +---- + 10 + 11 + 13 + 14 + 12 + 15 +(6 rows) + +explain (verbose, costs off) select * from cst_float8 join cst_float4 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_float8.a + -> Hash Join + Output: cst_float8.a + Hash Cond: (cst_float8.a = (cst_float4.a)::double precision) + -> Seq Scan on public.cst_float8 + Output: cst_float8.a + -> Hash + Output: cst_float4.a + -> Seq Scan on public.cst_float4 + Output: cst_float4.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_float8 join cst_float4 using(a); + a +---- + 10 + 6 + 7 + 8 + 9 + 11 + 12 + 13 + 14 + 15 + 5 +(11 rows) + +-- Confirm that casting logic works recursively +explain (verbose, costs off) +select * from cst_int2 + join cst_int8 using(a) + join cst_int4 using(a); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int8.a + -> Hash Join + Output: cst_int8.a + Hash Cond: (cst_int8.a = (cst_int4.a)::bigint) + -> Hash Join + Output: cst_int8.a + Hash Cond: ((cst_int2.a)::bigint = cst_int8.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(20 rows) + +select * from cst_int2 + join cst_int8 using(a) + join cst_int4 using(a); + a +---- + 10 +(1 row) + +reset optimizer_join_order; +-- BUG: this test is failing for ORCA. +-- It's the test that requires start_matchsubs. +explain (verbose, costs off) select * from cst_int2 left join cst_int4 using(a); +INFO: GPORCA failed to produce a plan, falling back to planner +DETAIL: CTranslatorQueryToDXL.cpp:3970: Failed assertion: ((((const Node*)(join_alias_node))->type) == T_Var) || ((((const Node*)(join_alias_node))->type) == T_CoalesceExpr) || ((((const Node*)(join_alias_node))->type) == T_RelabelType) + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: ((cst_int2.a)::integer) + -> Hash Left Join + Output: cst_int2.a + Hash Cond: (cst_int2.a = cst_int4.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Postgres query optimizer + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 left join cst_int4 using(a); +INFO: GPORCA failed to produce a plan, falling back to planner +DETAIL: CTranslatorQueryToDXL.cpp:3970: Failed assertion: ((((const Node*)(join_alias_node))->type) == T_Var) || ((((const Node*)(join_alias_node))->type) == T_CoalesceExpr) || ((((const Node*)(join_alias_node))->type) == T_RelabelType) + a +---- + 5 + 6 + 9 + 10 + 0 + 1 + 2 + 3 + 4 + 7 + 8 +(11 rows) + +explain (verbose, costs off) select * from cst_int2 right join cst_int4 using(a); + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Left Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = (cst_int2.a)::integer) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 right join cst_int4 using(a); + a +---- + 7 + 8 + 12 + 15 + 5 + 6 + 9 + 10 + 11 + 13 + 14 +(11 rows) + +explain (verbose, costs off) +select * from cst_int4 +where exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Semi Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = (cst_int2.a)::integer) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Filter: (NOT (cst_int4.a IS NULL)) + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(14 rows) + +select * from cst_int4 +where exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + a +---- + 7 + 8 + 5 + 6 + 9 + 10 +(6 rows) + +explain (verbose, costs off) +select * from cst_int4 +where not exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Anti Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = (cst_int2.a)::integer) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int4 +where not exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + a +---- + 12 + 15 + 11 + 13 + 14 +(5 rows) + +explain (verbose, costs off) +select * from cst_int4 +where cst_int4.a not in (select * from cst_int2); + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_int4.a + -> Hash Left Anti Semi (Not-In) Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = (cst_int2.a)::integer) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(15 rows) + +select * from cst_int4 +where cst_int4.a not in (select * from cst_int2); + a +---- + 12 + 15 + 11 + 13 + 14 +(5 rows) + +explain (verbose, costs off) +select * from cst_int2 natural join cst_int4; + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Hash Join + Output: cst_int4.a + Hash Cond: (cst_int4.a = (cst_int2.a)::integer) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 natural join cst_int4; + a +---- + 7 + 8 + 5 + 6 + 9 + 10 +(6 rows) + +-- Here, instead of an implicit cast, an explicit one is present. +-- The postgres-based planner should require a redistribution, because +-- distribution of the cst_int2 table is not directly equal to the left-hand side of the expression. +-- ORCA, on the other hand, can see that redistribution is unnecessary in this case. +explain (verbose, costs off) select * from cst_int2 join cst_int4 on cst_int2.a::int4 = cst_int4.a; + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int2.a, cst_int4.a + -> Hash Join + Output: cst_int2.a, cst_int4.a + Hash Cond: (cst_int4.a = (cst_int2.a)::integer) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2 join cst_int4 on cst_int2.a::int4 = cst_int4.a; + a | a +----+---- + 7 | 7 + 8 | 8 + 5 | 5 + 6 | 6 + 9 | 9 + 10 | 10 +(6 rows) + +-- The same thing, but with multiple casts in a row. +-- Because the first cast tends to be converted directly to a conversion function, +-- ORCA shouldn't be able to detect the first coercion and should require a redistribution. +explain (verbose, costs off) select * from cst_float4 as cst_float4_f join cst_float4 as cst_float4_s on cst_float4_f.a::int::float4 = cst_float4_s.a; + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_float4.a, cst_float4_1.a + -> Hash Join + Output: cst_float4.a, cst_float4_1.a + Hash Cond: ((int4(cst_float4.a))::real = cst_float4_1.a) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: cst_float4.a + Hash Key: (int4(cst_float4.a))::real + -> Seq Scan on public.cst_float4 + Output: cst_float4.a + -> Hash + Output: cst_float4_1.a + -> Seq Scan on public.cst_float4 cst_float4_1 + Output: cst_float4_1.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_float4 as cst_float4_f join cst_float4 as cst_float4_s on cst_float4_f.a::int::float4 = cst_float4_s.a; + a | a +----+---- + 10 | 10 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 + 9 | 9 + 11 | 11 + 12 | 12 + 13 | 13 + 14 | 14 + 15 | 15 +(11 rows) + +-- Check that binary coercible casts are ruled out +explain (verbose, costs off) select * from cst_varchar as foo join cst_varchar as bar using(a); + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_varchar.a + -> Hash Join + Output: cst_varchar.a + Hash Cond: ((cst_varchar.a)::text = (cst_varchar_1.a)::text) + -> Seq Scan on public.cst_varchar + Output: cst_varchar.a + -> Hash + Output: cst_varchar_1.a + -> Seq Scan on public.cst_varchar cst_varchar_1 + Output: cst_varchar_1.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_varchar as foo join cst_varchar as bar using(a); + a +---- + 9 + 10 + 11 + 12 + 13 + 14 + 5 + 6 + 8 + 15 + 7 +(11 rows) + +explain (verbose, costs off) select * from cst_varchar join cst_text using(a); + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_varchar.a + -> Hash Join + Output: cst_varchar.a + Hash Cond: ((cst_varchar.a)::text = cst_text.a) + -> Seq Scan on public.cst_varchar + Output: cst_varchar.a + -> Hash + Output: cst_text.a + -> Seq Scan on public.cst_text + Output: cst_text.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_varchar join cst_text using(a); + a +---- + 9 + 10 + 11 + 12 + 13 + 14 + 7 + 5 + 6 + 8 + 15 +(11 rows) + +-- Сheck that we don't rule out necessary distributions. +-- Most basic cases: +explain (verbose, costs off) select * from cst_float4 join cst_int4 using(a); + QUERY PLAN +---------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_float4.a + -> Hash Join + Output: cst_float4.a + Hash Cond: ((cst_float4.a)::double precision = (cst_int4.a)::double precision) + -> Seq Scan on public.cst_float4 + Output: cst_float4.a + -> Hash + Output: cst_int4.a + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: cst_int4.a + Hash Key: (cst_int4.a)::double precision + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_float4 join cst_int4 using(a); + a +---- + 10 + 5 + 6 + 7 + 8 + 9 + 11 + 12 + 13 + 14 + 15 +(11 rows) + +explain (verbose, costs off) select * from cst_int4 join cst_text on cst_int4.a = cst_text.a::int4; + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_int4.a, cst_text.a + -> Hash Join + Output: cst_int4.a, cst_text.a + Hash Cond: (cst_int4.a = (cst_text.a)::integer) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_text.a + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: cst_text.a + Hash Key: (cst_text.a)::integer + -> Seq Scan on public.cst_text + Output: cst_text.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_int4 join cst_text on cst_int4.a = cst_text.a::int4; + a | a +----+---- + 7 | 7 + 8 | 8 + 5 | 5 + 6 | 6 + 9 | 9 + 10 | 10 + 11 | 11 + 13 | 13 + 14 | 14 + 12 | 12 + 15 | 15 +(11 rows) + +-- ORCA: in order for there queries to work, equivalent expressions should be matched correctly +explain (verbose, costs off) +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (int8_cte as cte_2 join cst_int4 using(a)) using(a); + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: share0_ref2.a + -> Sequence + Output: share0_ref2.a + -> Shared Scan (share slice:id 1:0) + Output: share0_ref1.a + -> Materialize + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Hash Join + Output: share0_ref2.a + Hash Cond: (share0_ref3.a = (cst_int4.a)::bigint) + -> Hash Join + Output: share0_ref2.a, share0_ref3.a + Hash Cond: (share0_ref3.a = share0_ref2.a) + -> Shared Scan (share slice:id 1:0) + Output: share0_ref3.a + -> Hash + Output: share0_ref2.a + -> Hash Join + Output: share0_ref2.a + Hash Cond: (share0_ref2.a = (cst_int2.a)::bigint) + -> Shared Scan (share slice:id 1:0) + Output: share0_ref2.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Hash + Output: cst_int4.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(35 rows) + +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (int8_cte as cte_2 join cst_int4 using(a)) using(a); + a +---- + 10 +(1 row) + +explain (verbose, costs off) +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (cst_int4 join int8_cte as cte_2 using(a)) using(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: share0_ref3.a + -> Sequence + Output: share0_ref3.a + -> Shared Scan (share slice:id 1:0) + Output: share0_ref1.a + -> Materialize + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Hash Join + Output: share0_ref3.a + Hash Cond: ((share0_ref3.a = (cst_int2.a)::bigint) AND (share0_ref2.a = (cst_int4.a)::bigint) AND (share0_ref3.a = (cst_int4.a)::bigint) AND (share0_ref2.a = (cst_int2.a)::bigint)) + -> Hash Join + Output: share0_ref3.a, share0_ref2.a + Hash Cond: (share0_ref3.a = share0_ref2.a) + -> Shared Scan (share slice:id 1:0) + Output: share0_ref3.a + -> Hash + Output: share0_ref2.a + -> Shared Scan (share slice:id 1:0) + Output: share0_ref2.a + -> Hash + Output: cst_int2.a, cst_int4.a + -> Hash Join + Output: cst_int2.a, cst_int4.a + Hash Cond: (cst_int4.a = (cst_int2.a)::integer) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(35 rows) + +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (cst_int4 join int8_cte as cte_2 using(a)) using(a); + a +---- + 10 +(1 row) + +-- Test distribution by multiple keys +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int2_int4.a, cst_int2_int4.b, cst_int4_int8.a, cst_int4_int8.b + -> Hash Join + Output: cst_int2_int4.a, cst_int2_int4.b, cst_int4_int8.a, cst_int4_int8.b + Hash Cond: ((cst_int4_int8.a = (cst_int2_int4.a)::integer) AND (cst_int4_int8.b = (cst_int2_int4.b)::bigint)) + -> Seq Scan on public.cst_int4_int8 + Output: cst_int4_int8.a, cst_int4_int8.b + -> Hash + Output: cst_int2_int4.a, cst_int2_int4.b + -> Seq Scan on public.cst_int2_int4 + Output: cst_int2_int4.a, cst_int2_int4.b + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(13 rows) + +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); + a | b | a | b +----+----+----+---- + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 4 | 4 | 4 + 5 | 5 | 5 | 5 + 7 | 7 | 7 | 7 + 9 | 9 | 9 | 9 + 10 | 10 | 10 | 10 + 1 | 1 | 1 | 1 + 6 | 6 | 6 | 6 + 8 | 8 | 8 | 8 +(10 rows) + +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.b and t1.b = t2.a); + QUERY PLAN +------------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_int2_int4.a, cst_int2_int4.b, cst_int4_int8.a, cst_int4_int8.b + -> Hash Join + Output: cst_int2_int4.a, cst_int2_int4.b, cst_int4_int8.a, cst_int4_int8.b + Hash Cond: ((cst_int4_int8.b = (cst_int2_int4.a)::bigint) AND (cst_int4_int8.a = cst_int2_int4.b)) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: cst_int4_int8.a, cst_int4_int8.b + Hash Key: cst_int4_int8.b, cst_int4_int8.a + -> Seq Scan on public.cst_int4_int8 + Output: cst_int4_int8.a, cst_int4_int8.b + -> Hash + Output: cst_int2_int4.a, cst_int2_int4.b + -> Seq Scan on public.cst_int2_int4 + Output: cst_int2_int4.a, cst_int2_int4.b + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.b and t1.b = t2.a); + a | b | a | b +----+----+----+---- + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 4 | 4 | 4 + 5 | 5 | 5 | 5 + 7 | 7 | 7 | 7 + 9 | 9 | 9 | 9 + 10 | 10 | 10 | 10 + 1 | 1 | 1 | 1 + 6 | 6 | 6 | 6 + 8 | 8 | 8 | 8 +(10 rows) + +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int2 as t2 on (t1.a = t2.a); + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_int2_int4.a, cst_int2_int4.b, cst_int2.a + -> Hash Join + Output: cst_int2_int4.a, cst_int2_int4.b, cst_int2.a + Hash Cond: (cst_int2_int4.a = cst_int2.a) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Output: cst_int2_int4.a, cst_int2_int4.b + Hash Key: cst_int2_int4.a + -> Seq Scan on public.cst_int2_int4 + Output: cst_int2_int4.a, cst_int2_int4.b + -> Hash + Output: cst_int2.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_nestloop_factor=102400 +(16 rows) + +select * from cst_int2_int4 as t1 join cst_int2 as t2 on (t1.a = t2.a); + a | b | a +----+----+---- + 1 | 1 | 1 + 8 | 8 | 8 + 2 | 2 | 2 + 3 | 3 | 3 + 4 | 4 | 4 + 7 | 7 | 7 + 9 | 9 | 9 + 10 | 10 | 10 + 5 | 5 | 5 + 6 | 6 | 6 +(10 rows) + +set optimizer_join_order = query; +explain (verbose, costs off) +with cst_int2_int4_copy as (select * from cst_int2_int4) +select * from cst_int2_int4 + natural join cst_int4_int8 + natural join cst_int2_int4_copy; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4_int8.a, cst_int4_int8.b + -> Hash Join + Output: cst_int4_int8.a, cst_int4_int8.b + Hash Cond: ((cst_int4_int8.a = (cst_int2_int4_1.a)::integer) AND (cst_int4_int8.b = (cst_int2_int4_1.b)::bigint)) + -> Hash Join + Output: cst_int4_int8.a, cst_int4_int8.b + Hash Cond: (((cst_int2_int4.a)::integer = cst_int4_int8.a) AND ((cst_int2_int4.b)::bigint = cst_int4_int8.b)) + -> Seq Scan on public.cst_int2_int4 + Output: cst_int2_int4.a, cst_int2_int4.b + -> Hash + Output: cst_int4_int8.a, cst_int4_int8.b + -> Seq Scan on public.cst_int4_int8 + Output: cst_int4_int8.a, cst_int4_int8.b + -> Hash + Output: cst_int2_int4_1.a, cst_int2_int4_1.b + -> Seq Scan on public.cst_int2_int4 cst_int2_int4_1 + Output: cst_int2_int4_1.a, cst_int2_int4_1.b + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_mergejoin=off, enable_nestloop=off, optimizer_join_order=query, optimizer_nestloop_factor=102400 +(20 rows) + +with cst_int2_int4_copy as (select * from cst_int2_int4) +select * from cst_int2_int4 + natural join cst_int4_int8 + natural join cst_int2_int4_copy; + a | b +----+---- + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 7 | 7 + 9 | 9 + 10 | 10 + 1 | 1 + 6 | 6 + 8 | 8 +(10 rows) + +-- Test several queries with nested-loop join +reset optimizer_nestloop_factor; +set enable_nestloop = true; +set optimizer_enable_hashjoin = false; +set enable_hashjoin = false; +explain (verbose, costs off) select * from cst_int2 join cst_int4 using(a); + QUERY PLAN +----------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int4.a + -> Nested Loop + Output: cst_int4.a + Join Filter: (cst_int2.a = cst_int4.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on, optimizer_join_order=query +(11 rows) + +select * from cst_int2 join cst_int4 using(a); + a +---- + 7 + 8 + 5 + 6 + 9 + 10 +(6 rows) + +-- Subtle behavior ahead: in ORCA, nested loop joins don't support equivalent expressions +-- like hash joins do. +-- So, when join order is fixed, we need to be careful with the join condition, +-- otherwise we might get a broadcast motion. +explain (verbose, costs off) +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + QUERY PLAN +----------------------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + Output: cst_int2.a, cst_int4.a, cst_int8.a + -> Nested Loop + Output: cst_int2.a, cst_int4.a, cst_int8.a + Join Filter: (cst_int4.a = cst_int8.a) + -> Nested Loop + Output: cst_int2.a, cst_int4.a + Join Filter: (cst_int2.a = cst_int4.a) + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Materialize + Output: cst_int8.a + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: cst_int8.a + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on, optimizer_join_order=query +(20 rows) + +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + a | a | a +----+----+---- + 10 | 10 | 10 +(1 row) + +-- Luckily, when join order is not fixed, optimizer finds a join path that +-- gets rid of the broadcast. +reset optimizer_join_order; +explain (verbose, costs off) +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + QUERY PLAN +------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int2.a, cst_int4.a, cst_int8.a + -> Nested Loop + Output: cst_int2.a, cst_int4.a, cst_int8.a + Join Filter: (cst_int4.a = cst_int8.a) + -> Seq Scan on public.cst_int8 + Output: cst_int8.a + -> Nested Loop + Output: cst_int2.a, cst_int4.a + Join Filter: (cst_int2.a = cst_int4.a) + -> Seq Scan on public.cst_int4 + Output: cst_int4.a + -> Seq Scan on public.cst_int2 + Output: cst_int2.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on +(16 rows) + +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + a | a | a +----+----+---- + 10 | 10 | 10 +(1 row) + +-- Test very specific logic for a join on several keys with an index in ORCA +create index cst_int4_int8_idx on cst_int4_int8 (b); +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_int2_int4.a, cst_int2_int4.b, cst_int4_int8.a, cst_int4_int8.b + -> Nested Loop + Output: cst_int2_int4.a, cst_int2_int4.b, cst_int4_int8.a, cst_int4_int8.b + Join Filter: true + -> Seq Scan on public.cst_int2_int4 + Output: cst_int2_int4.a, cst_int2_int4.b + -> Index Scan using cst_int4_int8_idx on public.cst_int4_int8 + Output: cst_int4_int8.a, cst_int4_int8.b + Index Cond: (cst_int4_int8.b = cst_int2_int4.b) + Filter: (cst_int2_int4.a = cst_int4_int8.a) + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on +(13 rows) + +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); + a | b | a | b +----+----+----+---- + 2 | 2 | 2 | 2 + 3 | 3 | 3 | 3 + 4 | 4 | 4 | 4 + 5 | 5 | 5 | 5 + 7 | 7 | 7 | 7 + 9 | 9 | 9 | 9 + 10 | 10 | 10 | 10 + 1 | 1 | 1 | 1 + 6 | 6 | 6 | 6 + 8 | 8 | 8 | 8 +(10 rows) + +drop index cst_int4_int8_idx; +-- Binary coercible casts should be ruled out too +explain (verbose, costs off) select * from cst_varchar as foo join cst_varchar as bar using(a); + QUERY PLAN +--------------------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_varchar_1.a + -> Nested Loop + Output: cst_varchar_1.a + Join Filter: ((cst_varchar_1.a)::text = (cst_varchar.a)::text) + -> Seq Scan on public.cst_varchar cst_varchar_1 + Output: cst_varchar_1.a + -> Seq Scan on public.cst_varchar + Output: cst_varchar.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on +(11 rows) + +select * from cst_varchar as foo join cst_varchar as bar using(a); + a +---- + 7 + 5 + 6 + 8 + 15 + 9 + 10 + 11 + 12 + 13 + 14 +(11 rows) + +explain (verbose, costs off) select * from cst_varchar join cst_text using(a); + QUERY PLAN +------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + Output: cst_varchar.a + -> Nested Loop + Output: cst_varchar.a + Join Filter: ((cst_varchar.a)::text = cst_text.a) + -> Seq Scan on public.cst_varchar + Output: cst_varchar.a + -> Seq Scan on public.cst_text + Output: cst_text.a + Optimizer: Pivotal Optimizer (GPORCA) + Settings: enable_hashjoin=off, enable_mergejoin=off, enable_nestloop=on +(11 rows) + +select * from cst_varchar join cst_text using(a); + a +---- + 7 + 5 + 6 + 8 + 15 + 9 + 10 + 11 + 12 + 13 + 14 +(11 rows) + +drop table cst_int2; +drop table cst_int4; +drop table cst_int8; +drop table cst_float4; +drop table cst_float8; +drop table cst_text; +drop table cst_varchar; +drop table cst_int2_int4; +drop table cst_int4_int8; +reset optimizer_trace_fallback; +reset enable_hashjoin; +reset enable_nestloop; +reset enable_mergejoin; +reset optimizer_enable_hashjoin; +reset optimizer_nestloop_factor; +reset optimizer_enable_mergejoin; diff --git a/src/test/regress/expected/gporca_optimizer.out b/src/test/regress/expected/gporca_optimizer.out index 052ee4cf351c..5bc3ac9ce5a5 100644 --- a/src/test/regress/expected/gporca_optimizer.out +++ b/src/test/regress/expected/gporca_optimizer.out @@ -13439,21 +13439,23 @@ from foo l1 where b in (select ab -- 16 group by columns are not a superset of the distribution columns - no index scan explain (costs off) select * from foo join (select b, count(*) as cnt from tbtree group by b) grby on foo.a=grby.cnt; - QUERY PLAN ------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------ Gather Motion 3:1 (slice3; segments: 3) -> Nested Loop Join Filter: (foo.a = (count())) - -> Broadcast Motion 3:3 (slice2; segments: 3) - -> Seq Scan on foo + -> Seq Scan on foo -> Materialize - -> HashAggregate - Group Key: tbtree.b - -> Redistribute Motion 3:3 (slice1; segments: 3) - Hash Key: tbtree.b - -> Seq Scan on tbtree + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: (count()) + -> Result + -> HashAggregate + Group Key: tbtree.b + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: tbtree.b + -> Seq Scan on tbtree Optimizer: Pivotal Optimizer (GPORCA) -(12 rows) +(14 rows) -- 17 group by columns don't intersect - no index scan explain (costs off) diff --git a/src/test/regress/expected/join_optimizer.out b/src/test/regress/expected/join_optimizer.out index 4846cec6f8fd..ff7b63846985 100755 --- a/src/test/regress/expected/join_optimizer.out +++ b/src/test/regress/expected/join_optimizer.out @@ -3914,14 +3914,14 @@ select t1.* from on (t1.f1 = b1.d1) left join int4_tbl i4 on (i8.q2 = i4.f1); - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Gather Motion 3:1 (slice8; segments: 3) + QUERY PLAN +------------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice6; segments: 3) Output: text_tbl.f1 -> Hash Left Join Output: text_tbl.f1 Hash Cond: (int8_tbl.q2 = (int4_tbl_1.f1)::bigint) - -> Redistribute Motion 1:3 (slice6) + -> Redistribute Motion 1:3 (slice4) Output: text_tbl.f1, int8_tbl.q2 -> Hash Right Join Output: text_tbl.f1, int8_tbl.q2 @@ -3929,27 +3929,23 @@ select t1.* from -> Hash Right Join Output: "outer".d1, int8_tbl.q2 Hash Cond: (("outer".d2)::bigint = int8_tbl_2.q2) - -> Hash Left Join + -> Gather Motion 3:1 (slice1; segments: 3) Output: int8_tbl.q2, "outer".d2 - Hash Cond: (int8_tbl.q1 = int8_tbl_1.q1) - -> Gather Motion 3:1 (slice1; segments: 3) - Output: int8_tbl.q1, int8_tbl.q2 + -> Hash Left Join + Output: int8_tbl.q2, "outer".d2 + Hash Cond: (int8_tbl.q1 = int8_tbl_1.q1) -> Seq Scan on public.int8_tbl Output: int8_tbl.q1, int8_tbl.q2 - -> Hash - Output: "outer".d2, int8_tbl_1.q1 - -> Result - Output: NULL::integer, int8_tbl_1.q1 - -> Hash Join - Output: int8_tbl_1.q1 - Hash Cond: (int8_tbl_1.q1 = (int4_tbl.f1)::bigint) - -> Gather Motion 3:1 (slice2; segments: 3) + -> Hash + Output: "outer".d2, int8_tbl_1.q1 + -> Result + Output: NULL::integer, int8_tbl_1.q1 + -> Hash Join Output: int8_tbl_1.q1 + Hash Cond: (int8_tbl_1.q1 = (int4_tbl.f1)::bigint) -> Seq Scan on public.int8_tbl int8_tbl_1 Output: int8_tbl_1.q1 - -> Hash - Output: int4_tbl.f1 - -> Gather Motion 3:1 (slice3; segments: 3) + -> Hash Output: int4_tbl.f1 -> Seq Scan on public.int4_tbl Output: int4_tbl.f1 @@ -3957,25 +3953,25 @@ select t1.* from Output: "outer".d1, int8_tbl_2.q2 -> Result Output: '***'::text, int8_tbl_2.q2 - -> Gather Motion 3:1 (slice4; segments: 3) + -> Gather Motion 3:1 (slice2; segments: 3) Output: int8_tbl_2.q2 -> Seq Scan on public.int8_tbl int8_tbl_2 Output: int8_tbl_2.q2 -> Hash Output: text_tbl.f1 - -> Gather Motion 3:1 (slice5; segments: 3) + -> Gather Motion 3:1 (slice3; segments: 3) Output: text_tbl.f1 -> Seq Scan on public.text_tbl Output: text_tbl.f1 -> Hash Output: int4_tbl_1.f1 - -> Broadcast Motion 3:3 (slice7; segments: 3) + -> Broadcast Motion 3:3 (slice5; segments: 3) Output: int4_tbl_1.f1 -> Seq Scan on public.int4_tbl int4_tbl_1 Output: int4_tbl_1.f1 Optimizer: Pivotal Optimizer (GPORCA) Settings: enable_seqscan=on, optimizer=on -(59 rows) +(55 rows) select t1.* from text_tbl t1 diff --git a/src/test/regress/greengage_schedule b/src/test/regress/greengage_schedule index 35393aa2e4a9..08d6e20f2d1c 100755 --- a/src/test/regress/greengage_schedule +++ b/src/test/regress/greengage_schedule @@ -197,7 +197,7 @@ test: rpt rpt_joins rpt_tpch rpt_returning bfv_dml_rpt test: bfv_cte bfv_joins bfv_planner bfv_subquery bfv_legacy bfv_temp bfv_dml test: bfv_oom -test: qp_olap_mdqa qp_misc gp_recursive_cte qp_dml_joins qp_dml_oids trigger_sets_oid qp_skew qp_select +test: qp_olap_mdqa qp_misc gp_recursive_cte qp_dml_joins qp_dml_oids trigger_sets_oid qp_skew qp_select gp_cast test: qp_misc_jiras qp_with_clause qp_executor qp_olap_windowerr qp_olap_window qp_derived_table qp_bitmapscan qp_dropped_cols test: qp_with_functional_inlining qp_with_functional_noinlining diff --git a/src/test/regress/sql/gp_cast.sql b/src/test/regress/sql/gp_cast.sql new file mode 100644 index 000000000000..2ec9532a6175 --- /dev/null +++ b/src/test/regress/sql/gp_cast.sql @@ -0,0 +1,271 @@ +-- This file contains tests related to the casting behavior, primarily in joins. +-- Some of the tests performed here may already be present inside other files, +-- but it is nice to have a centralized place to observe and compare the behavior of the planners. +-- Tables here are prefixed with cst (which stands for 'cast') to avoid collisions. + +-- One of the tests below fails with an assertion, that prints its location in the source code. +-- Make sure that we don't need to update this file when its line changes. +-- start_matchsubs +-- m/DETAIL: CTranslatorQueryToDXL.cpp:\d+: Failed assertion/ +-- s/DETAIL: CTranslatorQueryToDXL.cpp:\d+: Failed assertion/DETAIL: CTranslatorQueryToDXL.cpp:###: Failed assertion/ +-- end_matchsubs + +set optimizer_enable_mergejoin = false; +-- We want to specifically test hashjoins for several queries below. +-- Nested loop joins are not constructed for these queries at the time, +-- and probably never will be, but let's make them additionally unlikely just in case. +set optimizer_nestloop_factor = 102400; +set enable_mergejoin = false; +set enable_nestloop = false; +set optimizer_trace_fallback = true; + +-- start_ignore +drop table if exists cst_int2; +drop table if exists cst_int4; +drop table if exists cst_int8; +drop table if exists cst_float4; +drop table if exists cst_float8; +drop table if exists cst_text; +drop table if exists cst_varchar; +drop table if exists cst_int2_int4; +drop table if exists cst_int4_int8; +-- end_ignore + +create table cst_int2 as (select a::int2 from generate_series(0, 10) as a) distributed by (a); +create table cst_int4 as (select a::int4 from generate_series(5, 15) as a) distributed by (a); +create table cst_int8 as (select a::int8 from generate_series(10, 20) as a) distributed by (a); +create table cst_float4 as (select a::float4 from generate_series(5, 15, 1) as a) distributed by (a); +create table cst_float8 as (select a::float8 from generate_series(5, 15, 1) as a) distributed by (a); +create table cst_text as (select a::text from generate_series(5, 15) as a) distributed by (a); +create table cst_varchar as (select a::varchar from generate_series(5, 15) as a) distributed by (a); +create table cst_int2_int4 as (select gen::int2 as a, gen::int4 as b from generate_series(1, 10) as gen) distributed by (a, b); +create table cst_int4_int8 as (select gen::int4 as a, gen::int8 as b from generate_series(1, 10) as gen) distributed by (a, b); + +-- Perform an inner join on all hash opfamilies containing more than one type. +-- We shouldn't see any redistributions, because: +-- The postgres-based planner has operators that work on different types. +-- ORCA doesn't support such operations, but it knows when a cast doesn't change a distribution. + +set optimizer_join_order = query; + +explain (verbose, costs off) select * from cst_int2 join cst_int4 using(a); +select * from cst_int2 join cst_int4 using(a); + +explain (verbose, costs off) select * from cst_int2 join cst_int8 using(a); +select * from cst_int2 join cst_int8 using(a); + +explain (verbose, costs off) select * from cst_int4 join cst_int8 using(a); +select * from cst_int4 join cst_int8 using(a); + +explain (verbose, costs off) select * from cst_float4 join cst_float8 using(a); +select * from cst_float4 join cst_float8 using(a); + +-- Same thing with the sides swapped +explain (verbose, costs off) select * from cst_int4 join cst_int2 using(a); +select * from cst_int4 join cst_int2 using(a); + +explain (verbose, costs off) select * from cst_int8 join cst_int2 using(a); +select * from cst_int8 join cst_int2 using(a); + +explain (verbose, costs off) select * from cst_int8 join cst_int4 using(a); +select * from cst_int8 join cst_int4 using(a); + +explain (verbose, costs off) select * from cst_float8 join cst_float4 using(a); +select * from cst_float8 join cst_float4 using(a); + +-- Confirm that casting logic works recursively +explain (verbose, costs off) +select * from cst_int2 + join cst_int8 using(a) + join cst_int4 using(a); + +select * from cst_int2 + join cst_int8 using(a) + join cst_int4 using(a); + +reset optimizer_join_order; + +-- BUG: this test is failing for ORCA. +-- It's the test that requires start_matchsubs. +explain (verbose, costs off) select * from cst_int2 left join cst_int4 using(a); +select * from cst_int2 left join cst_int4 using(a); + +explain (verbose, costs off) select * from cst_int2 right join cst_int4 using(a); +select * from cst_int2 right join cst_int4 using(a); + +explain (verbose, costs off) +select * from cst_int4 +where exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + +select * from cst_int4 +where exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + +explain (verbose, costs off) +select * from cst_int4 +where not exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + +select * from cst_int4 +where not exists (select * + from cst_int2 + where cst_int4.a = cst_int2.a); + +explain (verbose, costs off) +select * from cst_int4 +where cst_int4.a not in (select * from cst_int2); + +select * from cst_int4 +where cst_int4.a not in (select * from cst_int2); + +explain (verbose, costs off) +select * from cst_int2 natural join cst_int4; +select * from cst_int2 natural join cst_int4; + + +-- Here, instead of an implicit cast, an explicit one is present. +-- The postgres-based planner should require a redistribution, because +-- distribution of the cst_int2 table is not directly equal to the left-hand side of the expression. +-- ORCA, on the other hand, can see that redistribution is unnecessary in this case. +explain (verbose, costs off) select * from cst_int2 join cst_int4 on cst_int2.a::int4 = cst_int4.a; +select * from cst_int2 join cst_int4 on cst_int2.a::int4 = cst_int4.a; + + +-- The same thing, but with multiple casts in a row. +-- Because the first cast tends to be converted directly to a conversion function, +-- ORCA shouldn't be able to detect the first coercion and should require a redistribution. +explain (verbose, costs off) select * from cst_float4 as cst_float4_f join cst_float4 as cst_float4_s on cst_float4_f.a::int::float4 = cst_float4_s.a; +select * from cst_float4 as cst_float4_f join cst_float4 as cst_float4_s on cst_float4_f.a::int::float4 = cst_float4_s.a; + +-- Check that binary coercible casts are ruled out +explain (verbose, costs off) select * from cst_varchar as foo join cst_varchar as bar using(a); +select * from cst_varchar as foo join cst_varchar as bar using(a); + +explain (verbose, costs off) select * from cst_varchar join cst_text using(a); +select * from cst_varchar join cst_text using(a); + +-- Сheck that we don't rule out necessary distributions. +-- Most basic cases: +explain (verbose, costs off) select * from cst_float4 join cst_int4 using(a); +select * from cst_float4 join cst_int4 using(a); + +explain (verbose, costs off) select * from cst_int4 join cst_text on cst_int4.a = cst_text.a::int4; +select * from cst_int4 join cst_text on cst_int4.a = cst_text.a::int4; + + +-- ORCA: in order for there queries to work, equivalent expressions should be matched correctly +explain (verbose, costs off) +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (int8_cte as cte_2 join cst_int4 using(a)) using(a); + +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (int8_cte as cte_2 join cst_int4 using(a)) using(a); + +explain (verbose, costs off) +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (cst_int4 join int8_cte as cte_2 using(a)) using(a); + +with int8_cte as (select * from cst_int8) +select * from (cst_int2 join int8_cte as cte_1 using(a)) + join (cst_int4 join int8_cte as cte_2 using(a)) using(a); + + +-- Test distribution by multiple keys +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); + +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.b and t1.b = t2.a); +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.b and t1.b = t2.a); + +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int2 as t2 on (t1.a = t2.a); +select * from cst_int2_int4 as t1 join cst_int2 as t2 on (t1.a = t2.a); + +set optimizer_join_order = query; +explain (verbose, costs off) +with cst_int2_int4_copy as (select * from cst_int2_int4) +select * from cst_int2_int4 + natural join cst_int4_int8 + natural join cst_int2_int4_copy; + +with cst_int2_int4_copy as (select * from cst_int2_int4) +select * from cst_int2_int4 + natural join cst_int4_int8 + natural join cst_int2_int4_copy; + + +-- Test several queries with nested-loop join +reset optimizer_nestloop_factor; +set enable_nestloop = true; +set optimizer_enable_hashjoin = false; +set enable_hashjoin = false; + +explain (verbose, costs off) select * from cst_int2 join cst_int4 using(a); +select * from cst_int2 join cst_int4 using(a); + +-- Subtle behavior ahead: in ORCA, nested loop joins don't support equivalent expressions +-- like hash joins do. +-- So, when join order is fixed, we need to be careful with the join condition, +-- otherwise we might get a broadcast motion. +explain (verbose, costs off) +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + +-- Luckily, when join order is not fixed, optimizer finds a join path that +-- gets rid of the broadcast. +reset optimizer_join_order; +explain (verbose, costs off) +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + +select * from cst_int2 + join cst_int4 on cst_int2.a = cst_int4.a + join cst_int8 on cst_int4.a = cst_int8.a; + +-- Test very specific logic for a join on several keys with an index in ORCA +create index cst_int4_int8_idx on cst_int4_int8 (b); +explain (verbose, costs off) +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); +select * from cst_int2_int4 as t1 join cst_int4_int8 as t2 on (t1.a = t2.a and t1.b = t2.b); +drop index cst_int4_int8_idx; + +-- Binary coercible casts should be ruled out too +explain (verbose, costs off) select * from cst_varchar as foo join cst_varchar as bar using(a); +select * from cst_varchar as foo join cst_varchar as bar using(a); + +explain (verbose, costs off) select * from cst_varchar join cst_text using(a); +select * from cst_varchar join cst_text using(a); + + +drop table cst_int2; +drop table cst_int4; +drop table cst_int8; +drop table cst_float4; +drop table cst_float8; +drop table cst_text; +drop table cst_varchar; +drop table cst_int2_int4; +drop table cst_int4_int8; + +reset optimizer_trace_fallback; +reset enable_hashjoin; +reset enable_nestloop; +reset enable_mergejoin; +reset optimizer_enable_hashjoin; +reset optimizer_nestloop_factor; +reset optimizer_enable_mergejoin; From 6219d1bfa3fbe9fa199b2b8056e3855676a61176 Mon Sep 17 00:00:00 2001 From: Maxim Gajdaj Date: Wed, 4 Mar 2026 19:14:09 +0700 Subject: [PATCH 094/111] Enable gpexpand behave tests for 6.x (#296) * Retarget Behave tests CI to v24 tag * Mount sqldump/dump.sql to cdw service in docker compose * Enable gpexpand behave test Task: ADBDEV-9130 --- .github/workflows/greengage-ci.yml | 2 +- ci/docker-compose.yaml | 1 + gpMgmt/test/behave/mgmt_utils/gpexpand.feature | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/greengage-ci.yml b/.github/workflows/greengage-ci.yml index 7a2b689183a8..b3d307d6c4b4 100644 --- a/.github/workflows/greengage-ci.yml +++ b/.github/workflows/greengage-ci.yml @@ -41,7 +41,7 @@ jobs: contents: read # Explicit for default behavior packages: read # Explicit for GHCR access clarity actions: write # Required for artifact upload - uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v19 + uses: greengagedb/greengage-ci/.github/workflows/greengage-reusable-tests-behave.yml@v24 with: version: 6 target_os: ${{ matrix.target_os }} diff --git a/ci/docker-compose.yaml b/ci/docker-compose.yaml index 5d98e5a567ec..f2386f3f4232 100644 --- a/ci/docker-compose.yaml +++ b/ci/docker-compose.yaml @@ -16,6 +16,7 @@ services: hostname: cdw volumes: - ${PWD}/allure-results:/tmp/allure-results + - ${PWD}/sqldump/dump.sql:/home/gpadmin/sqldump/dump.sql - ${PWD}/ssh_keys:/home/gpadmin/.ssh.src - ${PWD}/logs_cdw:/logs sdw1: diff --git a/gpMgmt/test/behave/mgmt_utils/gpexpand.feature b/gpMgmt/test/behave/mgmt_utils/gpexpand.feature index 0da5e78ee59a..1bf817c00be4 100644 --- a/gpMgmt/test/behave/mgmt_utils/gpexpand.feature +++ b/gpMgmt/test/behave/mgmt_utils/gpexpand.feature @@ -363,7 +363,6 @@ Feature: expand the cluster by adding more segments When the user runs gpexpand to redistribute Then the numsegments of table "ext_test" is 4 - @skip @gpexpand_icw_db_concourse Scenario: Use a dump of the ICW database for expansion Given the database is not running From 8b18119c950f18f6f96edea19101e6e214c83ea3 Mon Sep 17 00:00:00 2001 From: Maxim Michkov Date: Fri, 6 Mar 2026 12:44:20 +0300 Subject: [PATCH 095/111] Restore backwards compatibility for isolation2 tests (#302) After commit 44e96ec948f9caa7c8c667716a6d6c2d71e29716, even after make install in isolation2 directory there were missing symlinks in the source directory. These symlinks were moved to installation directory, but to preserve backwards compatibility we should leave them in old place as well, so that isolation2 tests can still be run the old way, from source directory. Ticket: ADBDEV-9345 --- src/test/isolation2/Makefile | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/test/isolation2/Makefile b/src/test/isolation2/Makefile index 58f156894bf6..dfb61d2b2593 100644 --- a/src/test/isolation2/Makefile +++ b/src/test/isolation2/Makefile @@ -35,6 +35,21 @@ pg_regress.o: $(MAKE) -C $(top_builddir)/src/test/regress pg_regress.o rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/pg_regress.o . +gpstringsubs.pl: + rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/gpstringsubs.pl + +gpdiff.pl: atmsort.pm explain.pm GPTest.pm + rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/gpdiff.pl + +GPTest.pm: + rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/GPTest.pm + +atmsort.pm: + rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/atmsort.pm + +explain.pm: + rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/explain.pm + data: rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/data @@ -50,10 +65,11 @@ pg_isolation2_regress$(X): isolation2_main.o pg_regress.o submake-libpgport subm clean distclean: rm -f pg_isolation2_regress$(X) $(OBJS) isolation2_main.o isolation2_regress.so rm -f pg_regress.o test_parallel_retrieve_cursor_extended_query test_parallel_retrieve_cursor_extended_query_error + rm -f gpstringsubs.pl gpdiff.pl GPTest.pm atmsort.pm explain.pm rm -f data rm -rf $(pg_regress_clean_files) -install: all +install: all gpdiff.pl gpstringsubs.pl $(MKDIR_P) '$(DESTDIR)$(pgxsdir)/$(subdir)' $(INSTALL_PROGRAM) pg_isolation2_regress$(X) '$(DESTDIR)$(pgxsdir)/$(subdir)/pg_isolation2_regress$(X)' $(INSTALL_PROGRAM) sql_isolation_testcase.py '$(DESTDIR)$(pgxsdir)/$(subdir)/sql_isolation_testcase.py' From 20e4e1d7c5306786e83bbd08444eb7724dbbe85b Mon Sep 17 00:00:00 2001 From: Viktor Kurilko Date: Tue, 28 Oct 2025 19:33:31 +0700 Subject: [PATCH 096/111] Update PyGreSQL from 4.0 to 5.2.5 The following points have been fixed: 1. PyGreSQL 5 has added support for converting additional data types. Analyzedb: Converting datetime to a string for correct comparison with the value saved in the file. el8_migrate_localte.py, gparray.py, gpcatalog.py and gpcheckcat: using the Bool type instead of comparing with a string. gpcheckcat, repair_missing_extraneous.py and unique_index_violation_check: using python list instead of string parsing. 2. PyGreSQL 5 added support for closing a connection when using the with construct. Because of this, in a number of places, reading from the cursor took place after the connection was closed. 3. PyGreSQL 5 does not end the transaction if an error occurs, which leads to a possible connection leak if an error occurs in the connect function. So catch errors that happen in the connect function. 4. Add closure of the connection saved in context after the scenario in behave tests. 5. Add closure to the connection if it does not return from the function. 6. Use the python wrapper for the connect function instead of C one. 7. Use a custom cursor to disable row postprocessing to avoid correcting a large amount of code. 8. Fix the bool and array format in isolation2 tests. 9. Add notifications processing to isolation2 tests. 10. Also fix the notifications processing in the resgroup_query_mem test. 11. Fix the notifications processing in gpload. 12. Fix pg_config search when building deb packages. 13. Fix gpexpand behave tests (#176) The previous commit added a few regressions. The regression was related to replacing the comparison condition of the comparison with 't' with a truth check. This change is due to the fact that in PyGreSQL 5, unlike the 4th version, it converts the bool values. But it was not taken into account that such values can be set in Python code. The error of calling verify in TestDML has also been fixed. The verify method was called without passing a connection, and although the verify implementation in the class itself does not require a connection, this function may be overloaded in a child class. 14. Fix PyGreSQL install to be compatible with both python versions (#183) PyGreSQL install works in Python 2 but breaks in Python 3 because the _pg extension must be importable as a top-level module (e.g. from _pg import *). Python 3 resolves extension modules via sys.path, so _pg*.so has to be located at the sys.path root, not only inside the pygresql/ package directory. Move _pg*.so from pygresql directory to the top-level, so the same install layout works for both Python versions. Update _pg*.so RPATH to match its installed location so dpkg-shlibdeps can resolve libpq.so during Debian packaging. 15. Fix Python unit tests after PyGreSQL update (#222) - test_it_retries_the_connection: use mock object that support context managment - GpArrayTestCase: use bool type instead str 't'/'f' - GpCheckCatTestCase: check connection in DbWrapper. - DifferentialRecoveryClsTestCase and GpStopSmartModeTestCase: mock GgdbCursor to return connection. - RepairMissingExtraneousTestCase and UniqueIndexViolationCheckTestCase: use python arrays instead of string representation of Postgres arrays. Also fix seg ids set in get_segment_to_oid_mapping. Since seg ids in issues are now ints, we do not need to cast all_seg_ids array elements to strings. 16. Move PyGreSQL code to submodule (#269) It would be nice to avoid patching this module. Also this patch fixes Greengage installation scripts for PyGreSQL to support non-root release builds over DESTDIR. It was a problem of Greengage, non PyGreSQL. Additionally, include the new PyGreSQL license to the NOTICE file 17. Fix minirepro and gpsd utility for PyGreSQL-5.2.5 (#291) Both utils used outdated version of method pgdb.connect(). The patch changes the way pgdb.connect() is used by avoiding usage of parameter which later gets parsed. Instead both utils now use parameters of the same names. Co-authored-by: Denis Garsh Co-authored-by: Vasiliy Ivanov --- .gitmodules | 3 + NOTICE | 38 +- README.windows.md | 2 +- .../scripts/compile_gpdb_remote_windows.bat | 2 +- gpAux/Makefile | 2 +- gpMgmt/Makefile | 5 + gpMgmt/bin/Makefile | 4 +- gpMgmt/bin/analyzedb | 14 +- .../el8_migrate_locale/el8_migrate_locale.py | 4 +- gpMgmt/bin/gpcheckcat | 124 +- .../repair_missing_extraneous.py | 12 +- .../unique_index_violation_check.py | 2 +- gpMgmt/bin/gpinitstandby | 12 +- gpMgmt/bin/gpload.py | 22 +- gpMgmt/bin/gppylib/db/dbconn.py | 53 +- gpMgmt/bin/gppylib/gparray.py | 90 +- gpMgmt/bin/gppylib/gpcatalog.py | 2 +- .../gppylib/operations/buildMirrorSegments.py | 3 +- .../operations/segment_reconfigurer.py | 7 +- .../unit/test_unit_segment_reconfigurer.py | 7 +- .../bin/gppylib/programs/clsRecoverSegment.py | 2 +- gpMgmt/bin/gppylib/programs/clsSystemState.py | 28 +- .../gppylib/test/unit/test_unit_gparray.py | 13 +- .../gppylib/test/unit/test_unit_gpcheckcat.py | 15 +- .../test/unit/test_unit_gpsegrecovery.py | 12 +- .../bin/gppylib/test/unit/test_unit_gpstop.py | 6 +- .../test_unit_repair_missing_extraneous.py | 30 +- .../test_unit_unique_index_violation_check.py | 4 +- gpMgmt/bin/gpsd | 5 +- gpMgmt/bin/minirepro | 3 +- gpMgmt/bin/pythonSrc/PyGreSQL-4.0/.gitignore | 1 - .../bin/pythonSrc/PyGreSQL-4.0/CMakeLists.txt | 40 - .../pythonSrc/PyGreSQL-4.0/docs/announce.html | 28 - .../pythonSrc/PyGreSQL-4.0/docs/announce.txt | 23 - .../PyGreSQL-4.0/docs/changelog.html | 333 -- .../pythonSrc/PyGreSQL-4.0/docs/changelog.txt | 285 -- .../pythonSrc/PyGreSQL-4.0/docs/default.css | 279 -- .../bin/pythonSrc/PyGreSQL-4.0/docs/docs.css | 109 - .../pythonSrc/PyGreSQL-4.0/docs/future.html | 62 - .../pythonSrc/PyGreSQL-4.0/docs/future.txt | 48 - .../pythonSrc/PyGreSQL-4.0/docs/index.html | 182 - .../pythonSrc/PyGreSQL-4.0/docs/install.html | 198 - .../pythonSrc/PyGreSQL-4.0/docs/install.txt | 188 - .../bin/pythonSrc/PyGreSQL-4.0/docs/pg.html | 2429 ----------- gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pg.txt | 1382 ------ .../bin/pythonSrc/PyGreSQL-4.0/docs/pgdb.html | 51 - .../bin/pythonSrc/PyGreSQL-4.0/docs/pgdb.txt | 42 - .../pythonSrc/PyGreSQL-4.0/docs/readme.html | 243 -- .../pythonSrc/PyGreSQL-4.0/docs/readme.txt | 206 - gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pg.py | 711 ---- gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pgdb.py | 582 --- gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pgmodule.c | 3745 ----------------- gpMgmt/bin/pythonSrc/PyGreSQL-4.0/setup.py | 156 - .../PyGreSQL-4.0/tutorial/advanced.py | 198 - .../pythonSrc/PyGreSQL-4.0/tutorial/basics.py | 296 -- .../pythonSrc/PyGreSQL-4.0/tutorial/func.py | 205 - .../pythonSrc/PyGreSQL-4.0/tutorial/syscat.py | 149 - gpMgmt/bin/pythonSrc/PyGreSQL-5.2.5 | 1 + gpMgmt/sbin/gpgetstatususingtransition.py | 4 +- .../test/behave/mgmt_utils/analyzedb.feature | 2 +- gpMgmt/test/behave/mgmt_utils/environment.py | 3 + .../test/behave/mgmt_utils/gpreload.feature | 2 +- .../test/behave/mgmt_utils/minirepro.feature | 2 +- .../behave/mgmt_utils/steps/mgmt_utils.py | 46 +- .../steps/replication_slots_utils.py | 21 +- gpMgmt/test/behave_utils/cluster_expand.py | 4 +- gpMgmt/test/behave_utils/gpexpand_dml.py | 20 +- gpMgmt/test/behave_utils/utils.py | 8 +- .../expected/resgroup/resgroup_query_mem.out | 7 +- .../expected/resgroup/resgroup_views.out | 2 +- .../sql/resgroup/resgroup_query_mem.sql | 7 +- src/test/isolation2/sql_isolation_testcase.py | 20 +- 72 files changed, 381 insertions(+), 12465 deletions(-) delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/.gitignore delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/CMakeLists.txt delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/announce.html delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/announce.txt delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/changelog.html delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/changelog.txt delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/default.css delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/docs.css delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/future.html delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/future.txt delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/index.html delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/install.html delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/install.txt delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pg.html delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pg.txt delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pgdb.html delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pgdb.txt delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/readme.html delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/readme.txt delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pg.py delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pgdb.py delete mode 100644 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pgmodule.c delete mode 100755 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/setup.py delete mode 100755 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/advanced.py delete mode 100755 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/basics.py delete mode 100755 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/func.py delete mode 100755 gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/syscat.py create mode 160000 gpMgmt/bin/pythonSrc/PyGreSQL-5.2.5 diff --git a/.gitmodules b/.gitmodules index 1a1787514813..66c100cc7e2e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "gpcontrib/gpcloud/test/googletest"] path = gpcontrib/gpcloud/test/googletest url = https://github.com/google/googletest.git +[submodule "gpMgmt/bin/pythonSrc/PyGreSQL-5.2.5"] + path = gpMgmt/bin/pythonSrc/PyGreSQL-5.2.5 + url = https://github.com/PyGreSQL/PyGreSQL.git diff --git a/NOTICE b/NOTICE index 1bae549c9f76..1267a1240e33 100644 --- a/NOTICE +++ b/NOTICE @@ -1417,7 +1417,7 @@ distribute verbatim copies of this document is granted. ================================================================= - For: Python, PyGreSQL, argparse, python-subprocess32 + For: Python, argparse, python-subprocess32 ---------------------- Python License --------------------------- ================================================================= @@ -1471,6 +1471,42 @@ Agreement. +================================================================= +---------------------- PyGreSQL --------------------------------- +================================================================= + +Written by D'Arcy J.M. Cain (darcy@PyGreSQL.org) + +Based heavily on code written by Pascal Andre (andre@chimay.via.ecp.fr) + +Copyright (c) 1995, Pascal Andre + +Further modifications copyright (c) 1997-2008 by D'Arcy J.M. Cain + +Further modifications copyright (c) 2009-2023 by the PyGreSQL Development Team + +PyGreSQL is released under the PostgreSQL License, a liberal Open Source +license, similar to the BSD or MIT licenses: + +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 +this license the term "AUTHORS" refers to anyone who has contributed code +to PyGreSQL. + +IN NO EVENT SHALL THE AUTHORS 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 +AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +THE AUTHORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE +AUTHORS HAVE NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, +ENHANCEMENTS, OR MODIFICATIONS. + + ================================================================= ---------------------- pexpect ---------------------------------- ================================================================= diff --git a/README.windows.md b/README.windows.md index c2f84e5f117d..d407a395ecfa 100644 --- a/README.windows.md +++ b/README.windows.md @@ -142,7 +142,7 @@ cmake --build . --config Release --target INSTALL 4. Build pygresql, needed by gpload ``` -cd \gpMgmt\bin\pythonSrc\PyGreSQL-4.0 +cd \gpMgmt\bin\pythonSrc\PyGreSQL-5.2.5 mkdir build cd build cmake -DCMAKE_PREFIX_PATH=C:\greengage-db-devel -DCMAKE_INSTALL_PREFIX:PATH=C:\greengage-db-devel -G "Visual Studio 15 2017 Win64" .. diff --git a/concourse/scripts/compile_gpdb_remote_windows.bat b/concourse/scripts/compile_gpdb_remote_windows.bat index a053fa5b5365..8df936449c2e 100644 --- a/concourse/scripts/compile_gpdb_remote_windows.bat +++ b/concourse/scripts/compile_gpdb_remote_windows.bat @@ -15,7 +15,7 @@ cmake --build . --config Release --target ALL_BUILD cmake --build . --config Release --target INSTALL REM build pygresql -cd %WORK_DIR%\gpdb_src\gpMgmt\bin\pythonSrc\PyGreSQL-4.0 +cd %WORK_DIR%\gpdb_src\gpMgmt\bin\pythonSrc\PyGreSQL-5.2.5 mkdir build cd build cmake -DCMAKE_PREFIX_PATH=%WORK_DIR%\greengage-db-devel -DCMAKE_INSTALL_PREFIX:PATH=%WORK_DIR%\greengage-db-devel -G "Visual Studio 15 2017 Win64" .. diff --git a/gpAux/Makefile b/gpAux/Makefile index a7330c897669..109fa6e8b563 100644 --- a/gpAux/Makefile +++ b/gpAux/Makefile @@ -225,7 +225,7 @@ perl_archlibexp:=$(shell perl -MConfig -e 'print $$Config{archlibexp}') # set default build steps define BUILD_STEPS @rm -rf $(INSTLOC) - cd $(BUILDDIR) && PYGRESQL_LDFLAGS='-Wl,-rpath,\$$$$ORIGIN/../../../lib -Wl,--enable-new-dtags' $(MAKE) $(PARALLEL_MAKE_OPTS) install + cd $(BUILDDIR) && PYGRESQL_LDFLAGS='-Wl,-rpath,\$$$$ORIGIN/../../lib -Wl,--enable-new-dtags' $(MAKE) $(PARALLEL_MAKE_OPTS) install cd $(BUILDDIR)/src/pl/plpython && $(MAKE) clean && $(MAKE) $(PARALLEL_MAKE_OPTS) install && cd $(BUILDDIR) cd $(BUILDDIR)/src/pl/plperl && $(MAKE) clean && echo "LDFLAGS += -Wl,-rpath,$(perl_archlibexp)/CORE -Wl,--enable-new-dtags" >> GNUmakefile && echo "LDFLAGS_SL += -Wl,-rpath,$(perl_archlibexp)/CORE -Wl,--enable-new-dtags" >> GNUmakefile && $(MAKE) $(PARALLEL_MAKE_OPTS) install && cd $(BUILDDIR) #@$(MAKE) mgmtcopy INSTLOC=$(INSTLOC) diff --git a/gpMgmt/Makefile b/gpMgmt/Makefile index 206341707460..e95ff97f068b 100644 --- a/gpMgmt/Makefile +++ b/gpMgmt/Makefile @@ -21,8 +21,13 @@ install: generate_greengage_path_file if [ -e bin/ext/psutil ]; then \ cp -rp bin/ext/psutil $(DESTDIR)$(prefix)/lib/python ; \ fi + # Move _pg extension to sys.path root for Python 3 import (from _pg import *) if [ -e bin/ext/pygresql ]; then \ cp -rp bin/ext/pygresql $(DESTDIR)$(prefix)/lib/python ; \ + sofile=`find "$(DESTDIR)$(prefix)/lib/python/pygresql" -maxdepth 1 -type f -name "_pg*.so" | head -n 1`; \ + if [ -n "$$sofile" ]; then \ + mv -f "$$sofile" "$(DESTDIR)$(prefix)/lib/python/"; \ + fi; \ fi if [ -e bin/ext/yaml ]; then \ cp -rp bin/ext/yaml $(DESTDIR)$(prefix)/lib/python ; \ diff --git a/gpMgmt/bin/Makefile b/gpMgmt/bin/Makefile index 23acd9210709..49537e90b394 100644 --- a/gpMgmt/bin/Makefile +++ b/gpMgmt/bin/Makefile @@ -71,7 +71,7 @@ endif # # PyGreSQL # -PYGRESQL_VERSION=4.0 +PYGRESQL_VERSION=5.2.5 PYGRESQL_DIR=PyGreSQL-$(PYGRESQL_VERSION) pygresql: @@ -80,7 +80,7 @@ pygresql: unset LIBPATH; \ ./generate-greengage-path.sh > $(DESTDIR)$(prefix)/greengage_path.sh ; \ fi - PATH=$(bindir):$$PATH && \ + PATH=$(DESTDIR)/$(bindir):$$PATH && \ if [ `uname -s` = 'OpenBSD' ]; then \ cd $(PYLIB_SRC)/$(PYGRESQL_DIR) && DESTDIR="$(DESTDIR)" CC=cc python setup.py build; \ else \ diff --git a/gpMgmt/bin/analyzedb b/gpMgmt/bin/analyzedb index 9b8977e2bf48..0df9eb78cf21 100755 --- a/gpMgmt/bin/analyzedb +++ b/gpMgmt/bin/analyzedb @@ -175,9 +175,11 @@ def validate_schema_exists(pg_port, dbname, schema): def execute_sql(query, master_port, dbname): dburl = dbconn.DbURL(port=master_port, dbname=dbname) - conn = dbconn.connect(dburl) - cursor = dbconn.execSQL(conn, query) - return cursor.fetchall() + + with dbconn.connect(dburl) as conn: + cursor = dbconn.execSQL(conn, query) + result = cursor.fetchall() + return result def get_lines_from_file(fname): content = [] @@ -235,8 +237,8 @@ def get_partition_state_tuples(pg_port, dbname, catalog_schema, partition_info): logger.info("Table %s.%s (%s) no longer exists and will not be analyzed", schemaname, partition_name, tupletable) else: logger.error(str(e)) - # If there's an exception, the transaction is closed so we need to reconnect - conn = dbconn.connect(dburl) + # If there's an exception, the transaction is aborted so we need to rollback it + conn.rollback() else: num_sqls += 1 if num_sqls == 1000: # The choice of batch size was chosen arbitrarily @@ -697,7 +699,7 @@ class AnalyzeDb(Operation): qresult = run_sql(self.conn, GET_REQUESTED_LAST_OP_INFO_SQL % oid_str) ret = [] for r in qresult: - tup = (r[0], r[1], str(r[2]), r[3], r[4], r[5]) + tup = (r[0], r[1], str(r[2]), r[3], r[4], str(r[5])) ret.append(tup) return ret diff --git a/gpMgmt/bin/el8_migrate_locale/el8_migrate_locale.py b/gpMgmt/bin/el8_migrate_locale/el8_migrate_locale.py index 78ae50284367..9eec9dabf8a2 100644 --- a/gpMgmt/bin/el8_migrate_locale/el8_migrate_locale.py +++ b/gpMgmt/bin/el8_migrate_locale/el8_migrate_locale.py @@ -306,7 +306,7 @@ def dump_tables(self, fn): msg, size = self.get_table_size_info(dbname, parrelid) table_info.append((parrelid, tablename, coll, attname, msg, size)) # if no default partition, give a warning, in case of migrate failed - if has_default_partition == 'f': + if not has_default_partition: logger.warning("no default partition for {}".format(tablename)) else: # start multiple threads to check if the rows are still in the correct partitions after os upgrade, if check failed, add these tables to filtertabs @@ -432,7 +432,7 @@ def check_partitiontables_by_guc(self, idx, dbname): self.filtertabs.append((parrelid, tablename, coll, attname, msg, size)) self.filtertabslock.release() has_error = False - if has_default_partition == 'f': + if not has_default_partition: logger.warning("no default partition for {}".format(tablename)) db.query("set gp_detect_data_correctness = 0;") diff --git a/gpMgmt/bin/gpcheckcat b/gpMgmt/bin/gpcheckcat index f031fbb3a874..e57908cd68d4 100755 --- a/gpMgmt/bin/gpcheckcat +++ b/gpMgmt/bin/gpcheckcat @@ -27,6 +27,7 @@ import logging import re import sys import time +from contextlib import closing try: from gppylib import gplog @@ -202,16 +203,16 @@ def usage(exitarg=None): ############################### def getversion(): - db = connect() - curs = db.query(''' - select regexp_replace(current_setting('gp_server_version'), - E'([1-9]+.[0-9]+|main).*', - E'\\\\1') as ver;''') + with connect() as db: + curs = db.query(''' + select regexp_replace(current_setting('gp_server_version'), + E'([1-9]+.[0-9]+|main).*', + E'\\\\1') as ver;''') - row = curs.getresult()[0] - version = row[0] + row = curs.getresult()[0] + version = row[0] - logger.debug('got version %s' % version) + logger.debug('got version %s' % version) return version @@ -220,11 +221,11 @@ def getalldbs(): """ get all connectable databases """ - db = connect() - curs = db.query(''' - select datname from pg_database where datallowconn order by datname ''') - row = curs.getresult() - return row + with connect() as db: + curs = db.query(''' + select datname from pg_database where datallowconn order by datname ''') + row = curs.getresult() + return row ############################### @@ -347,8 +348,24 @@ def connect(user=None, password=None, host=None, port=None, (database, str(ex).strip())) exit(1) + class DbWrapper: + def __init__(self, db): + self.db = db + + def query(self, query): + return self.db.query(query) + + def close(self): + self.db.close() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.db.close() + logger.debug('connected with %s:%s %s' % (host, port, database)) - return db + return DbWrapper(db) ############# @@ -460,23 +477,22 @@ def formatErr(c, col, row): ############# def getGPConfiguration(): cfg = {} - db = connect() - # note that in 4.0, sql commands cannot be run against the segment mirrors directly - # so we filter out non-primary segment databases in the query - qry = ''' - SELECT content, preferred_role = 'p' as definedprimary, - dbid, role = 'p' as isprimary, hostname, address, port, - datadir - FROM gp_segment_configuration - WHERE (role = 'p' or content < 0 ) - ''' - curs = db.query(qry) - for row in curs.dictresult(): - if row['content'] == -1 and row['isprimary'] != 't': - continue # skip standby master - cfg[row['dbid']] = row - db.close() - return cfg + with connect() as db: + # note that in 4.0, sql commands cannot be run against the segment mirrors directly + # so we filter out non-primary segment databases in the query + qry = ''' + SELECT content, preferred_role = 'p' as definedprimary, + dbid, role = 'p' as isprimary, hostname, address, port, + datadir + FROM gp_segment_configuration + WHERE (role = 'p' or content < 0 ) + ''' + curs = db.query(qry) + for row in curs.dictresult(): + if row['content'] == -1 and not row['isprimary']: + continue # skip standby master + cfg[row['dbid']] = row + return cfg def checkDistribPolicy(): logger.info('-----------------------------------') @@ -3647,9 +3663,9 @@ def getClassOidForRelfilenode(relfilenode): qry = "SELECT oid FROM pg_class WHERE relfilenode = %d;" % (relfilenode) try: dburl = dbconn.DbURL(hostname=GV.opt['-h'], port=GV.opt['-p'], dbname=GV.dbname) - conn = dbconn.connect(dburl) - oid = dbconn.execSQLForSingletonRow(conn, qry)[0] - return oid + with dbconn.connect(dburl) as conn: + oid = dbconn.execSQLForSingletonRow(conn, qry)[0] + return oid except Exception, e: setError(ERROR_NOREPAIR) myprint(' Execution error: ' + str(e)) @@ -3669,10 +3685,10 @@ def getResourceTypeOid(oid): """ % (oid, oid) try: - db = connect() - curs = db.query(qry) - if len(curs.dictresult()) == 0: return 0 - return curs.dictresult().pop()['oid'] + with connect() as db: + curs = db.query(qry) + if len(curs.dictresult()) == 0: return 0 + return curs.dictresult().pop()['oid'] except Exception, e: setError(ERROR_NOREPAIR) myprint(' Execution error: ' + str(e)) @@ -3737,7 +3753,7 @@ def processMissingDuplicateEntryResult(catname, colname, allValues, type): # Note that if it was present in exactly half of the segments, # we will report it as both missing and extra. - ids = [int(i) for i in segids[1:-1].split(',')] + ids = [int(i) for i in segids] if len(ids) <= (GV.max_content + 2) / 2: issue = CatMissingIssue(catname, pkeys, segids, 'extra') @@ -3746,10 +3762,8 @@ def processMissingDuplicateEntryResult(catname, colname, allValues, type): if len(ids) >= (GV.max_content + 2) / 2: allids = [int(i) for i in range(-1, GV.max_content+1)] diffids = set(allids) - set(ids) - # convert the difference set back into a string that looks - # like a PostgreSQL array. - segids = ",".join(str(x) for x in diffids) - segids = "{%s}" % (segids) + + segids = diffids issue = CatMissingIssue(catname, pkeys, segids, 'missing') gpObj.addMissingIssue(issue) @@ -3912,8 +3926,8 @@ def processACLResult(catname, colname, allValues): oid = getOidFromPK(catname, pkeys) pkeys = {'oid': oid} - macl = row[-2][1:-1].split(',') if row[-2] != None else [] - sacl = row[-1][1:-1].split(',') if row[-1] != None else [] + macl = row[-2] if row[-2] is not None else [] + sacl = row[-1] if row[-1] is not None else [] macl_only = list(set(macl).difference(sacl)) sacl_only = list(set(sacl).difference(macl)) @@ -3933,10 +3947,10 @@ class CatInconsistentIssue: def report(self): def format_segids(segids): - if segids == '{NULL}': + if segids == [None]: idstr = 'all other segments' else: - ids = [int(i) for i in segids[1:-1].split(',')] + ids = [int(i) for i in segids] idstr = '' for i in ids: idstr += '%s (%s:%d) ' % \ @@ -3963,7 +3977,7 @@ class CatForeignKeyIssue: def report(self): - ids = [int(i) for i in self.segids[1:-1].split(',')] + ids = [int(i) for i in self.segids] idstr = '' if len(ids) == len(GV.report_cfg): idstr = 'all segments' @@ -3989,7 +4003,7 @@ class CatMissingIssue: def report(self): - ids = [int(i) for i in self.segids[1:-1].split(',')] + ids = [int(i) for i in self.segids] idstr = '' if len(ids) == len(GV.report_cfg): idstr = 'all segments' @@ -4255,16 +4269,16 @@ class GPObject: table_list = db.query(oid_query % issue.pkeys[key]).getresult() if table_list: if issue.type == 'missing': - GV.missing_attr_tables.append( (table_list[0][0], issue.segids) ) + GV.missing_attr_tables.append( (table_list[0][0], tuple(issue.segids)) ) else: - GV.extra_attr_tables.append( (table_list[0][0], issue.segids) ) + GV.extra_attr_tables.append( (table_list[0][0], tuple(issue.segids)) ) elif key == 'oid': table_list = db.query(type_query % issue.pkeys[key]).getresult() if table_list: if issue.type == 'missing': - GV.missing_attr_tables.append( (table_list[0][0], issue.segids) ) + GV.missing_attr_tables.append( (table_list[0][0], tuple(issue.segids)) ) else: - GV.extra_attr_tables.append( (table_list[0][0], issue.segids) ) + GV.extra_attr_tables.append( (table_list[0][0], tuple(issue.segids)) ) def __cmp__(self, other): @@ -4523,7 +4537,7 @@ def checkcatReport(): for part_table in db.query(partition_leaves_sql).getresult(): GV.missing_attr_tables.append( (part_table[0], segids) ) for table, segids in sorted(GV.missing_attr_tables): - for id in segids[1:-1].split(','): + for id in segids: myprint(" Table %s.%s.%s" % (GV.dbname, table, id)) myprint('') @@ -4537,7 +4551,7 @@ def checkcatReport(): for part_table in db.query(partition_leaves_sql).getresult(): GV.extra_attr_tables.append( (part_table[0], segids) ) for table, segids in sorted(GV.extra_attr_tables): - for id in segids[1:-1].split(','): + for id in segids: myprint(" Table %s.%s.%s" % (GV.dbname, table, id)) myprint('') myprint('') diff --git a/gpMgmt/bin/gpcheckcat_modules/repair_missing_extraneous.py b/gpMgmt/bin/gpcheckcat_modules/repair_missing_extraneous.py index dbd040dbe97d..5faabb05efd4 100644 --- a/gpMgmt/bin/gpcheckcat_modules/repair_missing_extraneous.py +++ b/gpMgmt/bin/gpcheckcat_modules/repair_missing_extraneous.py @@ -43,20 +43,20 @@ def get_segment_to_oid_mapping(self, all_seg_ids): return # issues look like this - # [(49401, "extra", '{1,2}'), - # (49401, "extra", '{1,2}')] + # [(49401, "extra", [1,2]), + # (49401, "extra", [1,2]) # OR - # [(49401, 'cmax', "extra", '{1,2}'), - # (49401, 'cmax', "extra", '{1,2}')] + # [(49401, 'cmax', "extra", [1,2]), + # (49401, 'cmax', "extra", [1,2]) - all_seg_ids = set([str(seg_id) for seg_id in all_seg_ids]) + all_seg_ids = set(all_seg_ids) oids_to_segment_mapping = {} for issue in self._issues: oid = issue[0] issue_type = issue[-2] - seg_ids = issue[-1].strip('{}').split(',') + seg_ids = issue[-1] # if an oid is missing from a segment(s) , then it is considered to be extra # on all the other segments/master diff --git a/gpMgmt/bin/gpcheckcat_modules/unique_index_violation_check.py b/gpMgmt/bin/gpcheckcat_modules/unique_index_violation_check.py index 8bf847a839cb..d1ca1d7599a3 100644 --- a/gpMgmt/bin/gpcheckcat_modules/unique_index_violation_check.py +++ b/gpMgmt/bin/gpcheckcat_modules/unique_index_violation_check.py @@ -39,7 +39,7 @@ def runCheck(self, db_connection): violations = [] for (table_oid, index_name, table_name, column_names) in unique_indexes: - column_names = column_names[1:-1] + column_names = ",".join(column_names) sql = self.get_violated_segments_query(table_name, column_names) violated_segments = db_connection.query(sql).getresult() if violated_segments: diff --git a/gpMgmt/bin/gpinitstandby b/gpMgmt/bin/gpinitstandby index 82a4cf3bd6a3..2a2dcd95ad54 100755 --- a/gpMgmt/bin/gpinitstandby +++ b/gpMgmt/bin/gpinitstandby @@ -321,12 +321,12 @@ def check_and_start_standby(): logger.error('Cannot use -n option when standby master has not yet been configured') raise GpInitStandbyException('Standby master not configured') - conn = dbconn.connect(dburl, utility=True) - sql = "SELECT * FROM pg_stat_replication" - cur = dbconn.execSQL(conn, sql) - if cur.rowcount >= 1: - logger.info("Standy master is already up and running.") - return + with dbconn.connect(dburl, utility=True) as conn: + sql = "SELECT * FROM pg_stat_replication" + cur = dbconn.execSQL(conn, sql) + if cur.rowcount >= 1: + logger.info("Standy master is already up and running.") + return standby = array.standbyMaster gp.start_standbymaster(standby.hostname, diff --git a/gpMgmt/bin/gpload.py b/gpMgmt/bin/gpload.py index 2c2a80a31386..273ccc2494d3 100755 --- a/gpMgmt/bin/gpload.py +++ b/gpMgmt/bin/gpload.py @@ -705,23 +705,13 @@ def quote_unident(val): return val -def notice_processor(self): - if windowsPlatform == True: - # We don't have a pygresql with our notice fix, so skip for windows. - # This means we will not get any warnings on windows (MPP10989). - return - theNotices = self.db.notices() +def notice_processor(notice): r = re.compile("^NOTICE: found (\d+) data formatting errors.*") - messageNumber = 0 - m = None - while messageNumber < len(theNotices) and m is None: - aNotice = theNotices[messageNumber] - m = r.match(aNotice) - messageNumber = messageNumber + 1 - if m: - global NUM_WARN_ROWS - NUM_WARN_ROWS = int(m.group(1)) + m = r.match(notice.message) + if m: + global NUM_WARN_ROWS + NUM_WARN_ROWS = int(m.group(1)) def handle_kill(signum, frame): # already dying? @@ -1829,6 +1819,7 @@ def setup_connection(self, recurse = 0): , user=self.options.U , passwd=self.options.password ) + self.db.set_notice_receiver(notice_processor) self.log(self.DEBUG, "Successfully connected to database") if withGpVersion == True: @@ -2698,7 +2689,6 @@ def create_staging_table(self): def count_errors(self): - notice_processor(self) if self.log_errors and not self.options.D: # make sure we only get errors for our own instance if not self.reuse_tables: diff --git a/gpMgmt/bin/gppylib/db/dbconn.py b/gpMgmt/bin/gppylib/db/dbconn.py index e7b5139e789c..13a1c70d4b1f 100644 --- a/gpMgmt/bin/gppylib/db/dbconn.py +++ b/gpMgmt/bin/gppylib/db/dbconn.py @@ -173,37 +173,30 @@ def connect(dburl, utility=False, verbose=False, dbhost = dburl.pghost dbport = int(dburl.pgport) dbopt = options - dbtty = "1" dbuser = dburl.pguser dbpasswd = dburl.pgpass timeout = dburl.timeout - cnx = None - - # All quotation and escaping here are to handle database name containing - # special characters like ' and \ and white spaces. - - # Need to escape backslashes and single quote in db name - # Also single quoted the connection string for dbname - dbbase = dbbase.replace('\\', '\\\\') - dbbase = dbbase.replace('\'', '\\\'') + conn_info = "Connecting to dbname='%s'" % dbbase + args = {} # MPP-14121, use specified connection timeout # Single quote the connection string for dbbase name if timeout is not None: - cstr = "dbname='%s' connect_timeout=%s" % (dbbase, timeout) + args["connect_timeout"] = timeout retries = dburl.retries + conn_info += " connect_timeout=%d" % timeout else: - cstr = "dbname='%s'" % dbbase retries = 1 # This flag helps to avoid logging the connection string in some special # situations as requested if (logConn == True): - (logger.info if timeout is not None else logger.debug)("Connecting to %s" % cstr) + (logger.info if timeout is not None else logger.debug)(conn_info) + conn = None for i in range(retries): try: - cnx = pgdb._connect_(cstr, dbhost, dbport, dbopt, dbtty, dbuser, dbpasswd) + conn = pgdb.connect(host="%s:%d" % (dbhost, dbport), database=dbbase, user=dbuser, password=dbpasswd, options=dbopt, **args) break except pgdb.InternalError, e: @@ -212,30 +205,21 @@ def connect(dburl, utility=False, verbose=False, continue raise - if cnx is None: + if conn is None: raise ConnectionError('Failed to connect to %s' % dbbase) # NOTE: the code to set ALWAYS_SECURE_SEARCH_PATH_SQL below assumes it is not part of an existing transaction - conn = pgdb.pgdbCnx(cnx) - #by default, libpq will print WARNINGS to stdout if not verbose: - cursor=conn.cursor() - cursor.execute("SET CLIENT_MIN_MESSAGES='ERROR'") - conn.commit() - cursor.close() + execSQLCloseConnectionOnError(conn, "SET CLIENT_MIN_MESSAGES='ERROR'") # set client encoding if needed if encoding: - cursor=conn.cursor() - cursor.execute("SET CLIENT_ENCODING='%s'" % encoding) - conn.commit() - cursor.close() + execSQLCloseConnectionOnError(conn, "SET CLIENT_ENCODING='%s'" % encoding) # unset search path due to CVE-2018-1058 if unsetSearchPath: - ALWAYS_SECURE_SEARCH_PATH_SQL = "SELECT pg_catalog.set_config('search_path', '', false)" - execSQL(conn, ALWAYS_SECURE_SEARCH_PATH_SQL).close() + execSQLCloseConnectionOnError(conn, "SELECT pg_catalog.set_config('search_path', '', false)") def __enter__(self): return self @@ -244,6 +228,10 @@ def __exit__(self, type, value, traceback): conn.__class__.__enter__, conn.__class__.__exit__ = __enter__, __exit__ return conn +class GgdbCursor(pgdb.Cursor): + # Do not process rows for compatibility with PyGreSQL 4 + def row_factory(self, row): + return row def execSQL(conn,sql): """ @@ -251,7 +239,7 @@ def execSQL(conn,sql): Do *NOT* violate that API here without considering the existing callers of this function. """ - cursor=conn.cursor() + cursor = GgdbCursor(conn) cursor.execute(sql) return cursor @@ -300,3 +288,12 @@ def executeUpdateOrInsert(conn, sql, expectedRowUpdatesOrInserts): raise Exception("SQL affected %s rows but %s were expected:\n%s" % \ (cursor.rowcount, expectedRowUpdatesOrInserts, sql)) return cursor + +def execSQLCloseConnectionOnError(conn, sql): + try: + with conn.cursor() as cursor: + cursor.execute(sql) + conn.commit() + except Exception as e: + conn.close() + raise e diff --git a/gpMgmt/bin/gppylib/gparray.py b/gpMgmt/bin/gppylib/gparray.py index d4f6232c4873..118cb0c96e35 100755 --- a/gpMgmt/bin/gppylib/gparray.py +++ b/gpMgmt/bin/gppylib/gparray.py @@ -524,7 +524,7 @@ def createSegmentRows( hostlist content = 0 for host in hostlist: - isprimary='t' + isprimary=True port=primary_portbase index = 0 for pdir in primary_list: @@ -555,7 +555,7 @@ def createSegmentRows( hostlist # this is a general problem for GPDB these days. # best to have the interface mapping stuff 1st. content=0 - isprimary='f' + isprimary=False num_hosts = len(hostlist) num_dirs=len(primary_list) if num_hosts <= num_dirs: @@ -610,7 +610,7 @@ def createSegmentRows( hostlist #we'll pick our mirror host to be 1 host "ahead" of the primary. mirror_host_offset = 1 - isprimary='f' + isprimary=False for host in hostlist: mirror_host = hostlist[mirror_host_offset % num_hosts] mirror_host_offset += 1 @@ -661,7 +661,7 @@ def createSegmentRowsFromSegmentList( newHostlist interfaceDict = {} for host in newHostlist: - isprimary='t' + isprimary=True port=primary_portbase index = 0 for pSeg in primary_segment_list: @@ -690,7 +690,7 @@ def createSegmentRowsFromSegmentList( newHostlist return rows elif mirror_type.lower().strip() == 'spread': content=0 - isprimary='f' + isprimary=False num_hosts = len(newHostlist) num_dirs=len(primary_segment_list) if num_hosts <= num_dirs: @@ -743,7 +743,7 @@ def createSegmentRowsFromSegmentList( newHostlist #we'll pick our mirror host to be 1 host "ahead" of the primary. mirror_host_offset = 1 - isprimary='f' + isprimary=False for host in newHostlist: mirror_host = newHostlist[mirror_host_offset % num_hosts] mirror_host_offset += 1 @@ -952,44 +952,44 @@ def initFromCatalog(dbURL, utility=False): """ hasMirrors = False - conn = dbconn.connect(dbURL, utility) - - # Get the version from the database: - version_str = None - for row in dbconn.execSQL(conn, "SELECT version()"): - version_str = row[0] - version = GpVersion(version_str) - if not version.isVersionCurrentRelease(): - raise Exception("Cannot connect to GPDB version %s from installed version %s"%(version.getVersionRelease(), MAIN_VERSION[0])) - - config_rows = dbconn.execSQL(conn, ''' - SELECT dbid, content, role, preferred_role, mode, status, - hostname, address, port, datadir - FROM pg_catalog.gp_segment_configuration - ORDER BY content, preferred_role DESC - ''') - - recoveredSegmentDbids = [] - segments = [] - seg = None - for row in config_rows: - - # Extract fields from the row - (dbid, content, role, preferred_role, mode, status, hostname, - address, port, datadir) = row - - # Check if segment mirrors exist - if preferred_role == ROLE_MIRROR and content != -1: - hasMirrors = True - - # If we have segments which have recovered, record them. - if preferred_role != role and content >= 0: - if mode == MODE_SYNCHRONIZED and status == STATUS_UP: - recoveredSegmentDbids.append(dbid) - - seg = Segment(content, preferred_role, dbid, role, mode, status, - hostname, address, port, datadir) - segments.append(seg) + with dbconn.connect(dbURL, utility) as conn: + + # Get the version from the database: + version_str = None + for row in dbconn.execSQL(conn, "SELECT version()"): + version_str = row[0] + version = GpVersion(version_str) + if not version.isVersionCurrentRelease(): + raise Exception("Cannot connect to GPDB version %s from installed version %s"%(version.getVersionRelease(), MAIN_VERSION[0])) + + config_rows = dbconn.execSQL(conn, ''' + SELECT dbid, content, role, preferred_role, mode, status, + hostname, address, port, datadir + FROM pg_catalog.gp_segment_configuration + ORDER BY content, preferred_role DESC + ''') + + recoveredSegmentDbids = [] + segments = [] + seg = None + for row in config_rows: + + # Extract fields from the row + (dbid, content, role, preferred_role, mode, status, hostname, + address, port, datadir) = row + + # Check if segment mirrors exist + if preferred_role == ROLE_MIRROR and content != -1: + hasMirrors = True + + # If we have segments which have recovered, record them. + if preferred_role != role and content >= 0: + if mode == MODE_SYNCHRONIZED and status == STATUS_UP: + recoveredSegmentDbids.append(dbid) + + seg = Segment(content, preferred_role, dbid, role, mode, status, + hostname, address, port, datadir) + segments.append(seg) origSegments = [seg.copy() for seg in segments] @@ -1719,7 +1719,7 @@ def _fixup_and_add_expansion_segments(self, rows, interface_list): # Add the new segment to the expansion segments array # Remove the content id off of the datadir new_datadir = row.fulldir[:row.fulldir.rfind(str(row.content))] - if row.isprimary == 't': + if row.isprimary: new_datadir += ('%d' % curr_content) self.addExpansionSeg(curr_content, ROLE_PRIMARY, curr_dbid, ROLE_PRIMARY, hostname, address, int(row.port), new_datadir) diff --git a/gpMgmt/bin/gppylib/gpcatalog.py b/gpMgmt/bin/gppylib/gpcatalog.py index de0913a0e75b..a87d5f5d81e6 100644 --- a/gpMgmt/bin/gppylib/gpcatalog.py +++ b/gpMgmt/bin/gppylib/gpcatalog.py @@ -156,7 +156,7 @@ def __init__(self, dbConnection): for [relname, relisshared] in curs.getresult(): self._tables[relname] = GPCatalogTable(self, relname) # Note: stupid API returns t/f for boolean value - self._tables[relname]._setShared(relisshared is 't') + self._tables[relname]._setShared(relisshared) # The tidycat.pl utility has been used to generate a json file # describing aspects of the catalog that we can not currently diff --git a/gpMgmt/bin/gppylib/operations/buildMirrorSegments.py b/gpMgmt/bin/gppylib/operations/buildMirrorSegments.py index b249e25c9b46..ff57449ae5a8 100644 --- a/gpMgmt/bin/gppylib/operations/buildMirrorSegments.py +++ b/gpMgmt/bin/gppylib/operations/buildMirrorSegments.py @@ -313,7 +313,8 @@ def _trigger_fts_probe(self, port=0): self.__logger.info('Triggering FTS probe') with dbconn.connect(dbconn.DbURL(port=port)) as conn: res = dbconn.execSQL(conn, "SELECT gp_request_fts_probe_scan()") - return res.fetchall() + return res.fetchall() + def _update_config(self, recovery_info_by_host, gpArray): # should use mainUtils.getProgramName but I can't make it work! programName = os.path.split(sys.argv[0])[-1] diff --git a/gpMgmt/bin/gppylib/operations/segment_reconfigurer.py b/gpMgmt/bin/gppylib/operations/segment_reconfigurer.py index 5382de909e5c..da9c6264536a 100644 --- a/gpMgmt/bin/gppylib/operations/segment_reconfigurer.py +++ b/gpMgmt/bin/gppylib/operations/segment_reconfigurer.py @@ -36,9 +36,9 @@ def reconfigure(self): # Empty block of 'BEGIN' and 'END' won't start a distributed transaction, # execute a DDL query to start a distributed transaction. # so the primaries'd better be up - conn = dbconn.connect(dburl) - conn.cursor().execute('CREATE TEMP TABLE temp_test(a int)') - conn.cursor().execute('COMMIT') + with dbconn.connect(dburl) as conn: + conn.cursor().execute('CREATE TEMP TABLE temp_test(a int)') + conn.cursor().execute('COMMIT') except Exception as e: now = time.time() if now < start_time + self.timeout: @@ -46,5 +46,4 @@ def reconfigure(self): else: raise RuntimeError("Mirror promotion did not complete in {0} seconds.".format(self.timeout)) else: - conn.close() break diff --git a/gpMgmt/bin/gppylib/operations/test/unit/test_unit_segment_reconfigurer.py b/gpMgmt/bin/gppylib/operations/test/unit/test_unit_segment_reconfigurer.py index 7726e3c836ea..eff453ce54eb 100644 --- a/gpMgmt/bin/gppylib/operations/test/unit/test_unit_segment_reconfigurer.py +++ b/gpMgmt/bin/gppylib/operations/test/unit/test_unit_segment_reconfigurer.py @@ -58,14 +58,17 @@ def test_it_triggers_fts_probe(self): ) def test_it_retries_the_connection(self): - self.connect.configure_mock(side_effect=[pgdb.DatabaseError, pgdb.DatabaseError, self.conn]) + ctx = MagicMock() + ctx.__enter__.return_value = self.conn + ctx.__exit__.return_value = False + + self.connect.configure_mock(side_effect=[pgdb.DatabaseError, pgdb.DatabaseError, ctx]) reconfigurer = SegmentReconfigurer(logger=self.logger, worker_pool=self.worker_pool, timeout=self.timeout) reconfigurer.reconfigure() self.connect.assert_has_calls([call(self.db_url), call(self.db_url), call(self.db_url), ]) - self.conn.close.assert_any_call() @patch('time.time') def test_it_gives_up_after_600_seconds(self, now_mock): diff --git a/gpMgmt/bin/gppylib/programs/clsRecoverSegment.py b/gpMgmt/bin/gppylib/programs/clsRecoverSegment.py index 631bc26f2875..eaabd43f6c05 100644 --- a/gpMgmt/bin/gppylib/programs/clsRecoverSegment.py +++ b/gpMgmt/bin/gppylib/programs/clsRecoverSegment.py @@ -230,7 +230,7 @@ def _get_dblist(self): # template0 does not accept any connections so we exclude it with dbconn.connect(dbconn.DbURL()) as conn: res = dbconn.execSQL(conn, "SELECT datname FROM PG_DATABASE WHERE datname != 'template0'") - return res.fetchall() + return res.fetchall() def run(self): if self.__options.parallelDegree < 1 or self.__options.parallelDegree > gp.MAX_MASTER_NUM_WORKERS: diff --git a/gpMgmt/bin/gppylib/programs/clsSystemState.py b/gpMgmt/bin/gppylib/programs/clsSystemState.py index bbf9aa3e8df3..5e791770fbc5 100644 --- a/gpMgmt/bin/gppylib/programs/clsSystemState.py +++ b/gpMgmt/bin/gppylib/programs/clsSystemState.py @@ -1426,20 +1426,20 @@ def __showStandbyMasterInformation(self, gpEnv, gpArray): logger.info("-------------------------------------------------------------" ) dbUrl = dbconn.DbURL(port=gpEnv.getMasterPort(), dbname='template1') - conn = dbconn.connect(dbUrl, utility=True) - sql = "SELECT state, sync_state, sent_location, flush_location, replay_location FROM pg_stat_replication" - cur = dbconn.execSQL(conn, sql) - if cur.rowcount == 1: - row = cur.fetchall()[0] - logger.info("-WAL Sender State: %s" % row[0]) - logger.info("-Sync state: %s" % row[1]) - logger.info("-Sent Location: %s" % row[2]) - logger.info("-Flush Location: %s" % row[3]) - logger.info("-Replay Location: %s" % row[4]) - elif cur.rowcount > 1: - logger.warning("pg_stat_replication shows more than 1 row.") - else: - logger.info("No entries found.") + with dbconn.connect(dbUrl, utility=True) as conn: + sql = "SELECT state, sync_state, sent_location, flush_location, replay_location FROM pg_stat_replication" + cur = dbconn.execSQL(conn, sql) + if cur.rowcount == 1: + row = cur.fetchall()[0] + logger.info("-WAL Sender State: %s" % row[0]) + logger.info("-Sync state: %s" % row[1]) + logger.info("-Sent Location: %s" % row[2]) + logger.info("-Flush Location: %s" % row[3]) + logger.info("-Replay Location: %s" % row[4]) + elif cur.rowcount > 1: + logger.warning("pg_stat_replication shows more than 1 row.") + else: + logger.info("No entries found.") logger.info("-------------------------------------------------------------" ) diff --git a/gpMgmt/bin/gppylib/test/unit/test_unit_gparray.py b/gpMgmt/bin/gppylib/test/unit/test_unit_gparray.py index b14780387695..ef925caf37cc 100755 --- a/gpMgmt/bin/gppylib/test/unit/test_unit_gparray.py +++ b/gpMgmt/bin/gppylib/test/unit/test_unit_gparray.py @@ -150,8 +150,8 @@ def test_get_segment_list(self): mirror_list, mirror_portbase, dir_prefix) offset = len(hostlist) * len(primary_list) # need to continue numbering where the last createSegmentRows left off for row in rows: - gparray.addExpansionSeg(row.content+offset, 'p' if convert_bool(row.isprimary) else 'm', row.dbid+offset, - 'p' if convert_bool(row.isprimary) else 'm', row.host, row.address, row.port, row.fulldir) + gparray.addExpansionSeg(row.content+offset, 'p' if row.isprimary else 'm', row.dbid+offset, + 'p' if row.isprimary else 'm', row.host, row.address, row.port, row.fulldir) self._validate_get_segment_list(gparray, hostlist, expansion_hosts, primary_list) @@ -197,9 +197,9 @@ def _setup_gparray(self, hostlist, interface_list, primary_list, primary_portbas for row in rows: newrow = Segment(content = row.content, - preferred_role = 'p' if convert_bool(row.isprimary) else 'm', + preferred_role = 'p' if row.isprimary else 'm', dbid = row.dbid, - role = 'p' if convert_bool(row.isprimary) else 'm', + role = 'p' if row.isprimary else 'm', mode = 's', status = 'u', hostname = row.host, @@ -377,11 +377,6 @@ def _createUnBalancedGpArrayWith2Primary2Mirrors(self): "6|-1|m|m|s|u|sdw3|sdw3|6432|/data/standby") return GpArray([self.coordinator, self.standby, self.primary0, self.primary1, self.mirror0, self.mirror1]) -def convert_bool(val): - if val == 't': - return True - else: - return False #------------------------------- Mainline -------------------------------- if __name__ == '__main__': diff --git a/gpMgmt/bin/gppylib/test/unit/test_unit_gpcheckcat.py b/gpMgmt/bin/gppylib/test/unit/test_unit_gpcheckcat.py index eaae6336aa25..b2e93be07e83 100755 --- a/gpMgmt/bin/gppylib/test/unit/test_unit_gpcheckcat.py +++ b/gpMgmt/bin/gppylib/test/unit/test_unit_gpcheckcat.py @@ -67,7 +67,10 @@ def test_running_unknown_check__raises_exception(self): def test_running_unique_index_violation_check__makes_the_check(self): self.subject.runOneCheck('unique_index_violation') - self.unique_index_violation_check.runCheck.assert_called_with(self.db_connection) + self.unique_index_violation_check.runCheck.assert_called() + (args, kwargs) = self.unique_index_violation_check.runCheck.call_args + wrapper = args[0] + self.assertIs(wrapper.db, self.db_connection) def test_running_unique_index_violation_check__when_no_violations_are_found__passes_the_check(self): self.subject.runOneCheck('unique_index_violation') @@ -111,7 +114,10 @@ def test_drop_leaked_schemas____when_leaked_schemas_exist__finds_and_drops_leake self.subject.drop_leaked_schemas(self.leaked_schema_dropper, self.db_connection) - self.leaked_schema_dropper.drop_leaked_schemas.assert_called_once_with(self.db_connection) + self.leaked_schema_dropper.drop_leaked_schemas.assert_called_once() + (args, kwargs) = self.leaked_schema_dropper.drop_leaked_schemas.call_args + wrapper = args[0] + self.assertIs(wrapper.db, self.db_connection) def test_drop_leaked_schemas__when_leaked_schemas_exist__passes_gpcheckcat(self): self.leaked_schema_dropper.drop_leaked_schemas.return_value = ['schema1', 'schema2'] @@ -191,7 +197,10 @@ def test_checkForeignKey__with_arg_gp_fastsequence(self, process_foreign_key_moc self.assertTrue(self.subject.GV.foreignKeyStatus) self.subject.setError.assert_any_call(self.subject.ERROR_REMOVE) self.foreign_key_check.runCheck.assert_called_once_with(cat_tables) - fast_seq_mock.assert_called_once_with(self.db_connection) + fast_seq_mock.assert_called_once() + (args, kwargs) = fast_seq_mock.call_args + wrapper = args[0] + self.assertIs(wrapper.db, self.db_connection) @patch('gpcheckcat.processForeignKeyResult') def test_checkForeignKey__with_arg(self, process_foreign_key_mock): diff --git a/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegrecovery.py b/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegrecovery.py index e3c1a8a62ae1..cb062c578346 100644 --- a/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegrecovery.py +++ b/gpMgmt/bin/gppylib/test/unit/test_unit_gpsegrecovery.py @@ -483,7 +483,9 @@ def test_pg_stop_backup_success(self, mock1, mock2): return_value='/data/mytblspace1/2') @patch('os.listdir') @patch('os.symlink') - def test_sync_tablespaces_outside_data_dir(self, mock1,mock2,mock3,mock4): + @patch('gppylib.db.dbconn.GgdbCursor', + side_effect=lambda conn: conn.cursor()) + def test_sync_tablespaces_outside_data_dir(self, mock1,mock2,mock3,mock4,mock5): self.diff_recovery_cmd.sync_tablespaces() self.assertEqual(2, self.mock_rsync_init.call_count) self.assertEqual(2, self.mock_rsync_run.call_count) @@ -495,7 +497,9 @@ def test_sync_tablespaces_outside_data_dir(self, mock1,mock2,mock3,mock4): return_value=[['1234','/data/primary0']]) @patch('os.listdir') @patch('os.symlink') - def test_sync_tablespaces_within_data_dir(self, mock, mock2,mock3): + @patch('gppylib.db.dbconn.GgdbCursor', + side_effect=lambda conn: conn.cursor()) + def test_sync_tablespaces_within_data_dir(self, mock, mock2,mock3,mock4): self.diff_recovery_cmd.sync_tablespaces() self.assertEqual(0, self.mock_rsync_init.call_count) self.assertEqual(0, self.mock_rsync_run.call_count) @@ -508,7 +512,9 @@ def test_sync_tablespaces_within_data_dir(self, mock, mock2,mock3): return_value='/data/mytblspace1/2') @patch('os.listdir') @patch('os.symlink') - def test_sync_tablespaces_mix_data_dir(self, mock1, mock2, mock3,mock4): + @patch('gppylib.db.dbconn.GgdbCursor', + side_effect=lambda conn: conn.cursor()) + def test_sync_tablespaces_mix_data_dir(self, mock1, mock2, mock3, mock4, mock5): self.diff_recovery_cmd.sync_tablespaces() self.assertEqual(1, self.mock_rsync_init.call_count) self.assertEqual(1, self.mock_rsync_run.call_count) diff --git a/gpMgmt/bin/gppylib/test/unit/test_unit_gpstop.py b/gpMgmt/bin/gppylib/test/unit/test_unit_gpstop.py index b8e6c6a66c58..df1bc122edd1 100644 --- a/gpMgmt/bin/gppylib/test/unit/test_unit_gpstop.py +++ b/gpMgmt/bin/gppylib/test/unit/test_unit_gpstop.py @@ -587,7 +587,8 @@ def _call(args, **kwargs): subprocess_call.side_effect = _call - def test_stop_master_smart_issues_pg_ctl_stop(self, subprocess_call, dbconn_connect): + @patch('gppylib.db.dbconn.GgdbCursor', side_effect=lambda conn: conn.cursor()) + def test_stop_master_smart_issues_pg_ctl_stop(self, cursor, subprocess_call, dbconn_connect): self._setup_subprocess(subprocess_call) self.gpstop.master_datadir = 'datadir' @@ -601,7 +602,8 @@ def test_stop_master_smart_raises_exception_if_stop_fails(self, subprocess_call, with self.assertRaises(Exception): self.gpstop._stop_master_smart() - def test_stop_master_smart_calls_pg_ctl_status_until_server_stops(self, subprocess_call, dbconn_connect): + @patch('gppylib.db.dbconn.GgdbCursor', side_effect=lambda conn: conn.cursor()) + def test_stop_master_smart_calls_pg_ctl_status_until_server_stops(self, cursor, subprocess_call, dbconn_connect): self._setup_subprocess(subprocess_call) self.gpstop.conn = dbconn_connect diff --git a/gpMgmt/bin/gppylib/test/unit/test_unit_repair_missing_extraneous.py b/gpMgmt/bin/gppylib/test/unit/test_unit_repair_missing_extraneous.py index f9f4ae277d35..bceefb19b4c0 100644 --- a/gpMgmt/bin/gppylib/test/unit/test_unit_repair_missing_extraneous.py +++ b/gpMgmt/bin/gppylib/test/unit/test_unit_repair_missing_extraneous.py @@ -13,13 +13,13 @@ def setUp(self): self.catalog_table_obj.getTableName.return_value = self.table_name def test_get_segment_to_oid_mapping_with_both_extra_and_missing(self): - issues = [(49401, "extra", '{1,2}'), - (49401, "extra", '{1,2}'), - (49402, "missing", '{2,3}'), - (49403, "extra", '{2,3}'), - (49404, "missing", '{1}'), - (49405, "extra", '{2,3}'), - (49406, "missing", '{2}')] + issues = [(49401, "extra", [1,2]), + (49401, "extra", [1,2]), + (49402, "missing", [2,3]), + (49403, "extra", [2,3]), + (49404, "missing", [1]), + (49405, "extra", [2,3]), + (49406, "missing", [2])] self.subject = RepairMissingExtraneous(self.catalog_table_obj, issues, "attrelid") repair_sql_contents = self.subject.get_segment_to_oid_mapping(self.all_seg_ids) @@ -32,10 +32,10 @@ def test_get_segment_to_oid_mapping_with_both_extra_and_missing(self): self.assertEqual(repair_sql_contents[3], set([49403, 49404, 49405, 49406])) def test_get_segment_to_oid_mapping_with_only_extra(self): - issues = [(49401, 'cmax', "extra", '{1,2}'), - (49401, 'cmax', "extra", '{1,2}'), - (49403, 'cmax', "extra", '{2,3}'), - (49405, 'cmax', "extra", '{2,3}')] + issues = [(49401, 'cmax', "extra", [1,2]), + (49401, 'cmax', "extra", [1,2]), + (49403, 'cmax', "extra", [2,3]), + (49405, 'cmax', "extra", [2,3])] self.subject = RepairMissingExtraneous(self.catalog_table_obj, issues, "attrelid") repair_sql_contents = self.subject.get_segment_to_oid_mapping(self.all_seg_ids) @@ -46,10 +46,10 @@ def test_get_segment_to_oid_mapping_with_only_extra(self): self.assertEqual(repair_sql_contents[3], set([49403, 49405])) def test_get_segment_to_oid_mapping_with_only_missing(self): - issues = [(49401, 'cmax', "missing", '{1,2}'), - (49401, 'cmax', "missing", '{1,2}'), - (49403, 'cmax', "missing", '{2,3}'), - (49405, 'cmax', "missing", '{2,3}')] + issues = [(49401, 'cmax', "missing", [1,2]), + (49401, 'cmax', "missing", [1,2]), + (49403, 'cmax', "missing", [2,3]), + (49405, 'cmax', "missing", [2,3])] self.subject = RepairMissingExtraneous(self.catalog_table_obj, issues, "attrelid") repair_sql_contents = self.subject.get_segment_to_oid_mapping(self.all_seg_ids) diff --git a/gpMgmt/bin/gppylib/test/unit/test_unit_unique_index_violation_check.py b/gpMgmt/bin/gppylib/test/unit/test_unit_unique_index_violation_check.py index 045f9e41fa17..745377ecac85 100644 --- a/gpMgmt/bin/gppylib/test/unit/test_unit_unique_index_violation_check.py +++ b/gpMgmt/bin/gppylib/test/unit/test_unit_unique_index_violation_check.py @@ -10,8 +10,8 @@ def setUp(self): self.index_query_result = Mock() self.index_query_result.getresult.return_value = [ - (9001, 'index1', 'table1', '{index1_column1,index1_column2}'), - (9001, 'index2', 'table1', '{index2_column1,index2_column2}') + (9001, 'index1', 'table1', ['index1_column1', 'index1_column2']), + (9001, 'index2', 'table1', ['index2_column1', 'index2_column2']) ] self.violated_segments_query_result = Mock() diff --git a/gpMgmt/bin/gpsd b/gpMgmt/bin/gpsd index 8982b9ec577c..ad1e54adfef7 100755 --- a/gpMgmt/bin/gpsd +++ b/gpMgmt/bin/gpsd @@ -230,10 +230,9 @@ def main(): # setup the connection info tuple with options connectionInfo = (host, port, user, db) - connectionString = ':'.join([host, port, db, user, '', pgoptions, '']) num_segments = 0 - with closing(pgdb.connect(connectionString)) as connection: + with pgdb.connect(host="%s:%s" % (host, port), database=db, user=user, options=pgoptions) as connection: with closing(connection.cursor()) as cursor: num_segments = get_num_segments(cursor) @@ -269,7 +268,7 @@ def main(): sys.stdout.flush() try: - with closing(pgdb.connect(connectionString)) as connection: + with pgdb.connect(host="%s:%s" % (host, port), database=db, user=user, options=pgoptions) as connection: with closing(connection.cursor()) as cursor: dumpTupleCount(cursor) dumpStats(cursor, inclHLL) diff --git a/gpMgmt/bin/minirepro b/gpMgmt/bin/minirepro index 839bbc674b55..c87aea6a2cb1 100755 --- a/gpMgmt/bin/minirepro +++ b/gpMgmt/bin/minirepro @@ -361,9 +361,8 @@ def main(): # setup the connection info tuple with options connectionInfo = (host, port, user, db) - connectionString = ':'.join([host, port, db, user, '', pgoptions, '']) print "Connecting to database: host=%s, port=%s, user=%s, db=%s ..." % connectionInfo - conn = pgdb.connect(connectionString) + conn = pgdb.connect(host="%s:%s" % (host, port), database=db, user=user, options=pgoptions) cursor = conn.cursor() # get server version, which is dumped to minirepro output file diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/.gitignore b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/.gitignore deleted file mode 100644 index 378eac25d311..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/CMakeLists.txt b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/CMakeLists.txt deleted file mode 100644 index 6f6ce0bdc1a8..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 3.12) - -set (CMAKE_CONFIGURATION_TYPES Release RelWithDebInfo) -project(pygresql C) - -find_package(Python2 COMPONENTS Interpreter Development) -if (Python2_FOUND) - include_directories(${Python2_INCLUDE_DIRS}) -else () - message(FATAL_ERROR "python2 not found") -endif(Python2_FOUND) - -add_definitions("/D FRONTEND") - -set (CPPFLAGS "/MP /wd4996 /wd4018 /wd4090 /wd4102 /wd4244 /wd4267 /wd4273 /wd4715") -add_definitions("${CPPFLAGS}") - -file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/__init__.py" "") -set(GPDB_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../) -include_directories(${GPDB_SRC_DIR}/src/include - ${GPDB_SRC_DIR}/src/interfaces/libpq - ${GPDB_SRC_DIR}/src/include/port - ${GPDB_SRC_DIR}/src/include/port/win32 - ${GPDB_SRC_DIR}/src/include/port/win32_msvc - ${GPDB_SRC_DIR}/src/port - ${Python2_INCLUDE_DIRS}) -link_directories(${CMAKE_PREFIX_PATH}/lib) -find_library(LIBPQ NAMES libpq HINTS ${CMAKE_INSTALL_PREFIX}/LIB) -find_library(LIBPGPORT NAMES libpgport HINTS ${CMAKE_INSTALL_PREFIX}/LIB) -find_library(LIBPGCOMMON NAMES libpgcommon HINTS ${CMAKE_INSTALL_PREFIX}/LIB) - -add_library (pygresql SHARED pgmodule.c) -target_link_libraries(pygresql ${LIBPQ} ${LIBPGPORT} ${LIBPGCOMMON} ws2_32 secur32 ${Python2_LIBRARIES}) - -set_target_properties(pygresql PROPERTIES OUTPUT_NAME "_pg") -set_target_properties(pygresql PROPERTIES SUFFIX ".pyd") -install(TARGETS pygresql DESTINATION lib/python/pygresql) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/pg.py DESTINATION lib/python/pygresql) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/pgdb.py DESTINATION lib/python/pygresql) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py DESTINATION lib/python/pygresql) \ No newline at end of file diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/announce.html b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/announce.html deleted file mode 100644 index 2683a15297d3..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/announce.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - -PyGreSQL Announcements - - - -
-

PyGreSQL Announcements

-

Release of PyGreSQL version 4.0

-

PyGreSQL v4.0 has been released.

-

It is available at: ftp://ftp.PyGreSQL.org/pub/distrib/PyGreSQL-4.0.tgz.

-

If you are running NetBSD, look in the packages directory under databases. -There is also a package in the FreeBSD ports collection.

-

Please refer to changelog.txt -for things that have changed in this version.

-

Please refer to readme.txt -for general information.

-
-
D'Arcy J.M. Cain
- -
-
- - diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/announce.txt b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/announce.txt deleted file mode 100644 index fa0845855aa9..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/announce.txt +++ /dev/null @@ -1,23 +0,0 @@ -====================== -PyGreSQL Announcements -====================== - -------------------------------- -Release of PyGreSQL version 4.0 -------------------------------- - -PyGreSQL v4.0 has been released. - -It is available at: ftp://ftp.PyGreSQL.org/pub/distrib/PyGreSQL-4.0.tgz. - -If you are running NetBSD, look in the packages directory under databases. -There is also a package in the FreeBSD ports collection. - -Please refer to `changelog.txt `_ -for things that have changed in this version. - -Please refer to `readme.txt `_ -for general information. - -| D'Arcy J.M. Cain -| darcy@PyGreSQL.org diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/changelog.html b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/changelog.html deleted file mode 100644 index 24c38e9fde9d..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/changelog.html +++ /dev/null @@ -1,333 +0,0 @@ - - - - - - -PyGreSQL ChangeLog - - - -
-

PyGreSQL ChangeLog

-
-

Version 4.0 (2009-01-01)

-
    -
  • Dropped support for Python below 2.3 and PostgreSQL below 7.4.
  • -
  • Improved performance of fetchall() for large result sets -by speeding up the type casts (as suggested by Peter Schuller).
  • -
  • Exposed exceptions as attributes of the connection object.
  • -
  • Exposed connection as attribute of the cursor object.
  • -
  • Cursors now support the iteration protocol.
  • -
  • Added new method to get parameter settings.
  • -
  • Added customizable row_factory as suggested by Simon Pamies.
  • -
  • Separated between mandatory and additional type objects.
  • -
  • Added keyword args to insert, update and delete methods.
  • -
  • Added exception handling for direct copy.
  • -
  • Release the GIL while making a connection -(as suggested by Peter Schuller).
  • -
  • If available, use decimal.Decimal for numeric types.
  • -
  • Allow DB wrapper to be used with DB-API 2 connections -(as suggested by Chris Hilton).
  • -
  • Made private attributes of DB wrapper accessible.
  • -
  • Dropped dependence on mx.DateTime module.
  • -
  • Support for PQescapeStringConn() and PQescapeByteaConn(); -these are now also used by the internal _quote() functions.
  • -
  • Added 'int8' to INTEGER types. New SMALLINT type.
  • -
  • Added a way to find the number of rows affected by a query() -with the classic pg module by returning it as a string. -For single inserts, query() still returns the oid as an integer. -The pgdb module already provides the "rowcount" cursor attribute -for the same purpose.
  • -
  • Improved getnotify() by calling PQconsumeInput() instead of -submitting an empty command.
  • -
  • Removed compatibility code for old OID munging style.
  • -
  • The insert() and update() methods now use the "returning" clause -if possible to get all changed values, and they also check in advance -whether a subsequent select is possible, so that ongoing transactions -won't break if there is no select privilege.
  • -
  • Added "protocol_version" and "server_version" attributes.
  • -
  • Revived the "user" attribute.
  • -
  • The pg module now works correctly with composite primary keys; -these are represented as frozensets.
  • -
  • Removed the undocumented and actually unnecessary "view" parameter -from the get() method.
  • -
  • get() raises a nicer ProgrammingError instead of a KeyError -if no primary key was found.
  • -
  • delete() now also works based on the primary key if no oid available -and returns whether the row existed or not.
  • -
-
-
-

Version 3.8.1 (2006-06-05)

-
    -
  • Use string methods instead of deprecated string functions.
  • -
  • Only use SQL-standard way of escaping quotes.
  • -
  • Added the functions escape_string() and escape/unescape_bytea() -(as suggested by Charlie Dyson and Kavous Bojnourdi a long time ago).
  • -
  • Reverted code in clear() method that set date to current.
  • -
  • Added code for backwards compatibility in OID munging code.
  • -
  • Reorder attnames tests so that "interval" is checked for before "int."
  • -
  • If caller supplies key dictionary, make sure that all has a namespace.
  • -
-
-
-

Version 3.8 (2006-02-17)

-
    -
  • Installed new favicon.ico from Matthew Sporleder <mspo@mspo.com>
  • -
  • Replaced snprintf by PyOS_snprintf.
  • -
  • Removed NO_SNPRINTF switch which is not needed any longer
  • -
  • Clean up some variable names and namespace
  • -
  • Add get_relations() method to get any type of relation
  • -
  • Rewrite get_tables() to use get_relations()
  • -
  • Use new method in get_attnames method to get attributes of views as well
  • -
  • Add Binary type
  • -
  • Number of rows is now -1 after executing no-result statements
  • -
  • Fix some number handling
  • -
  • Non-simple types do not raise an error any more
  • -
  • Improvements to documentation framework
  • -
  • Take into account that nowadays not every table must have an oid column
  • -
  • Simplification and improvement of the inserttable() function
  • -
  • Fix up unit tests
  • -
  • The usual assortment of minor fixes and enhancements
  • -
-
-
-

Version 3.7 (2005-09-07)

-

Improvement of pgdb module:

-
    -
  • Use Python standard datetime if mxDateTime is not available
  • -
-

Major improvements and clean-up in classic pg module:

-
    -
  • All members of the underlying connection directly available in DB
  • -
  • Fixes to quoting function
  • -
  • Add checks for valid database connection to methods
  • -
  • Improved namespace support, handle search_path correctly
  • -
  • Removed old dust and unnessesary imports, added docstrings
  • -
  • Internal sql statements as one-liners, smoothed out ugly code
  • -
-
-
-

Version 3.6.2 (2005-02-23)

-
    -
  • Further fixes to namespace handling
  • -
-
-
-

Version 3.6.1 (2005-01-11)

-
    -
  • Fixes to namespace handling
  • -
-
-
-

Version 3.6 (2004-12-17)

-
    -
  • Better DB-API 2.0 compliance
  • -
  • Exception hierarchy moved into C module and made available to both APIs
  • -
  • Fix error in update method that caused false exceptions
  • -
  • Moved to standard exception hierarchy in classic API
  • -
  • Added new method to get transaction state
  • -
  • Use proper Python constants where appropriate
  • -
  • Use Python versions of strtol, etc. Allows Win32 build.
  • -
  • Bug fixes and cleanups
  • -
-
-
-

Version 3.5 (2004-08-29)

-

Fixes and enhancements:

-
    -
  • Add interval to list of data types
  • -
  • fix up method wrapping especially close()
  • -
  • retry pkeys once if table missing in case it was just added
  • -
  • wrap query method separately to handle debug better
  • -
  • use isinstance instead of type
  • -
  • fix free/PQfreemem issue - finally
  • -
  • miscellaneous cleanups and formatting
  • -
-
-
-

Version 3.4 (2004-06-02)

-

Some cleanups and fixes. -This is the first version where PyGreSQL is moved back out of the -PostgreSQL tree. A lot of the changes mentioned below were actually -made while in the PostgreSQL tree since their last release.

-
    -
  • Allow for larger integer returns
  • -
  • Return proper strings for true and false
  • -
  • Cleanup convenience method creation
  • -
  • Enhance debugging method
  • -
  • Add reopen method
  • -
  • Allow programs to preload field names for speedup
  • -
  • Move OID handling so that it returns long instead of int
  • -
  • Miscellaneous cleanups and formatting
  • -
-
-
-

Version 3.3 (2001-12-03)

-

A few cleanups. Mostly there was some confusion about the latest version -and so I am bumping the number to keep it straight.

-
    -
  • Added NUMERICOID to list of returned types. This fixes a bug when -returning aggregates in the latest version of PostgreSQL.
  • -
-
-
-

Version 3.2 (2001-06-20)

-

Note that there are very few changes to PyGreSQL between 3.1 and 3.2. -The main reason for the release is the move into the PostgreSQL -development tree. Even the WIN32 changes are pretty minor.

- -
-
-

Version 3.1 (2000-11-06)

-
    -
  • Fix some quoting functions. In particular handle NULLs better.
  • -
  • Use a method to add primary key information rather than direct -manipulation of the class structures
  • -
  • Break decimal out in _quote (in pg.py) and treat it as float
  • -
  • Treat timestamp like date for quoting purposes
  • -
  • Remove a redundant SELECT from the get method speeding it, -and insert (since it calls get) up a little.
  • -
  • Add test for BOOL type in typecast method to pgdbTypeCache class -(tv@beamnet.de)
  • -
  • Fix pgdb.py to send port as integer to lower level function -(dildog@l0pht.com)
  • -
  • Change pg.py to speed up some operations
  • -
  • Allow updates on tables with no primary keys
  • -
-
-
-

Version 3.0 (2000-05-30)

- -
-
-

Version 2.4 (1999-06-15)

-
    -
  • Insert returns None if the user doesn't have select permissions -on the table. It can (and does) happen that one has insert but -not select permissions on a table.
  • -
  • Added ntuples() method to query object (brit@druid.net)
  • -
  • Corrected a bug related to getresult() and the money type
  • -
  • Corrected a bug related to negative money amounts
  • -
  • Allow update based on primary key if munged oid not available and -table has a primary key
  • -
  • Add many __doc__ strings (andre@via.ecp.fr)
  • -
  • Get method works with views if key specified
  • -
-
-
-

Version 2.3 (1999-04-17)

-
    -
  • connect.host returns "localhost" when connected to Unix socket -(torppa@tuhnu.cutery.fi)
  • -
  • Use PyArg_ParseTupleAndKeywords in connect() (torppa@tuhnu.cutery.fi)
  • -
  • fixes and cleanups (torppa@tuhnu.cutery.fi)
  • -
  • Fixed memory leak in dictresult() (terekhov@emc.com)
  • -
  • Deprecated pgext.py - functionality now in pg.py
  • -
  • More cleanups to the tutorial
  • -
  • Added fileno() method - terekhov@emc.com (Mikhail Terekhov)
  • -
  • added money type to quoting function
  • -
  • Compiles cleanly with more warnings turned on
  • -
  • Returns PostgreSQL error message on error
  • -
  • Init accepts keywords (Jarkko Torppa)
  • -
  • Convenience functions can be overridden (Jarkko Torppa)
  • -
  • added close() method
  • -
-
-
-

Version 2.2 (1998-12-21)

-
    -
  • Added user and password support thanks to Ng Pheng Siong (ngps@post1.com)
  • -
  • Insert queries return the inserted oid
  • -
  • Add new pg wrapper (C module renamed to _pg)
  • -
  • Wrapped database connection in a class
  • -
  • Cleaned up some of the tutorial. (More work needed.)
  • -
  • Added version and __version__. -Thanks to thilo@eevolute.com for the suggestion.
  • -
-
-
-

Version 2.1 (1998-03-07)

-
    -
  • return fields as proper Python objects for field type
  • -
  • Cleaned up pgext.py
  • -
  • Added dictresult method
  • -
-
-
-

Version 2.0 (1997-12-23)

-
    -
  • Updated code for PostgreSQL 6.2.1 and Python 1.5
  • -
  • Reformatted code and converted to use full ANSI style prototypes
  • -
  • Changed name to PyGreSQL (from PyGres95)
  • -
  • Changed order of arguments to connect function
  • -
  • Created new type pgqueryobject and moved certain methods to it
  • -
  • Added a print function for pgqueryobject
  • -
  • Various code changes - mostly stylistic
  • -
-
-
-

Version 1.0b (1995-11-04)

-
    -
  • Keyword support for connect function moved from library file to C code -and taken away from library
  • -
  • Rewrote documentation
  • -
  • Bug fix in connect function
  • -
  • Enhancements in large objects interface methods
  • -
-
-
-

Version 1.0a (1995-10-30)

-

A limited release.

-
    -
  • Module adapted to standard Python syntax
  • -
  • Keyword support for connect function in library file
  • -
  • Rewrote default parameters interface (internal use of strings)
  • -
  • Fixed minor bugs in module interface
  • -
  • Redefinition of error messages
  • -
-
-
-

Version 0.9b (1995-10-10)

-

The first public release.

-
    -
  • Large objects implementation
  • -
  • Many bug fixes, enhancements, ...
  • -
-
-
-

Version 0.1a (1995-10-07)

-
    -
  • Basic libpq functions (SQL access)
  • -
-
-
- - diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/changelog.txt b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/changelog.txt deleted file mode 100644 index ea0d71d1ff58..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/changelog.txt +++ /dev/null @@ -1,285 +0,0 @@ -================== -PyGreSQL ChangeLog -================== - -Version 4.0 (2009-01-01) ------------------------- -- Dropped support for Python below 2.3 and PostgreSQL below 7.4. -- Improved performance of fetchall() for large result sets - by speeding up the type casts (as suggested by Peter Schuller). -- Exposed exceptions as attributes of the connection object. -- Exposed connection as attribute of the cursor object. -- Cursors now support the iteration protocol. -- Added new method to get parameter settings. -- Added customizable row_factory as suggested by Simon Pamies. -- Separated between mandatory and additional type objects. -- Added keyword args to insert, update and delete methods. -- Added exception handling for direct copy. -- Release the GIL while making a connection - (as suggested by Peter Schuller). -- If available, use decimal.Decimal for numeric types. -- Allow DB wrapper to be used with DB-API 2 connections - (as suggested by Chris Hilton). -- Made private attributes of DB wrapper accessible. -- Dropped dependence on mx.DateTime module. -- Support for PQescapeStringConn() and PQescapeByteaConn(); - these are now also used by the internal _quote() functions. -- Added 'int8' to INTEGER types. New SMALLINT type. -- Added a way to find the number of rows affected by a query() - with the classic pg module by returning it as a string. - For single inserts, query() still returns the oid as an integer. - The pgdb module already provides the "rowcount" cursor attribute - for the same purpose. -- Improved getnotify() by calling PQconsumeInput() instead of - submitting an empty command. -- Removed compatibility code for old OID munging style. -- The insert() and update() methods now use the "returning" clause - if possible to get all changed values, and they also check in advance - whether a subsequent select is possible, so that ongoing transactions - won't break if there is no select privilege. -- Added "protocol_version" and "server_version" attributes. -- Revived the "user" attribute. -- The pg module now works correctly with composite primary keys; - these are represented as frozensets. -- Removed the undocumented and actually unnecessary "view" parameter - from the get() method. -- get() raises a nicer ProgrammingError instead of a KeyError - if no primary key was found. -- delete() now also works based on the primary key if no oid available - and returns whether the row existed or not. - - -Version 3.8.1 (2006-06-05) --------------------------- -- Use string methods instead of deprecated string functions. -- Only use SQL-standard way of escaping quotes. -- Added the functions escape_string() and escape/unescape_bytea() - (as suggested by Charlie Dyson and Kavous Bojnourdi a long time ago). -- Reverted code in clear() method that set date to current. -- Added code for backwards compatibility in OID munging code. -- Reorder attnames tests so that "interval" is checked for before "int." -- If caller supplies key dictionary, make sure that all has a namespace. - -Version 3.8 (2006-02-17) ------------------------- -- Installed new favicon.ico from Matthew Sporleder -- Replaced snprintf by PyOS_snprintf. -- Removed NO_SNPRINTF switch which is not needed any longer -- Clean up some variable names and namespace -- Add get_relations() method to get any type of relation -- Rewrite get_tables() to use get_relations() -- Use new method in get_attnames method to get attributes of views as well -- Add Binary type -- Number of rows is now -1 after executing no-result statements -- Fix some number handling -- Non-simple types do not raise an error any more -- Improvements to documentation framework -- Take into account that nowadays not every table must have an oid column -- Simplification and improvement of the inserttable() function -- Fix up unit tests -- The usual assortment of minor fixes and enhancements - -Version 3.7 (2005-09-07) ------------------------- -Improvement of pgdb module: - -- Use Python standard `datetime` if `mxDateTime` is not available - -Major improvements and clean-up in classic pg module: - -- All members of the underlying connection directly available in `DB` -- Fixes to quoting function -- Add checks for valid database connection to methods -- Improved namespace support, handle `search_path` correctly -- Removed old dust and unnessesary imports, added docstrings -- Internal sql statements as one-liners, smoothed out ugly code - -Version 3.6.2 (2005-02-23) --------------------------- -- Further fixes to namespace handling - -Version 3.6.1 (2005-01-11) --------------------------- -- Fixes to namespace handling - -Version 3.6 (2004-12-17) ------------------------- -- Better DB-API 2.0 compliance -- Exception hierarchy moved into C module and made available to both APIs -- Fix error in update method that caused false exceptions -- Moved to standard exception hierarchy in classic API -- Added new method to get transaction state -- Use proper Python constants where appropriate -- Use Python versions of strtol, etc. Allows Win32 build. -- Bug fixes and cleanups - -Version 3.5 (2004-08-29) ------------------------- -Fixes and enhancements: - -- Add interval to list of data types -- fix up method wrapping especially close() -- retry pkeys once if table missing in case it was just added -- wrap query method separately to handle debug better -- use isinstance instead of type -- fix free/PQfreemem issue - finally -- miscellaneous cleanups and formatting - -Version 3.4 (2004-06-02) ------------------------- -Some cleanups and fixes. -This is the first version where PyGreSQL is moved back out of the -PostgreSQL tree. A lot of the changes mentioned below were actually -made while in the PostgreSQL tree since their last release. - -- Allow for larger integer returns -- Return proper strings for true and false -- Cleanup convenience method creation -- Enhance debugging method -- Add reopen method -- Allow programs to preload field names for speedup -- Move OID handling so that it returns long instead of int -- Miscellaneous cleanups and formatting - -Version 3.3 (2001-12-03) ------------------------- -A few cleanups. Mostly there was some confusion about the latest version -and so I am bumping the number to keep it straight. - -- Added NUMERICOID to list of returned types. This fixes a bug when - returning aggregates in the latest version of PostgreSQL. - -Version 3.2 (2001-06-20) ------------------------- -Note that there are very few changes to PyGreSQL between 3.1 and 3.2. -The main reason for the release is the move into the PostgreSQL -development tree. Even the WIN32 changes are pretty minor. - -- Add Win32 support (gerhard@bigfoot.de) -- Fix some DB-API quoting problems (niall.smart@ebeon.com) -- Moved development into PostgreSQL development tree. - -Version 3.1 (2000-11-06) ------------------------- -- Fix some quoting functions. In particular handle NULLs better. -- Use a method to add primary key information rather than direct - manipulation of the class structures -- Break decimal out in `_quote` (in pg.py) and treat it as float -- Treat timestamp like date for quoting purposes -- Remove a redundant SELECT from the `get` method speeding it, - and `insert` (since it calls `get`) up a little. -- Add test for BOOL type in typecast method to `pgdbTypeCache` class - (tv@beamnet.de) -- Fix pgdb.py to send port as integer to lower level function - (dildog@l0pht.com) -- Change pg.py to speed up some operations -- Allow updates on tables with no primary keys - -Version 3.0 (2000-05-30) ------------------------- -- Remove strlen() call from pglarge_write() and get size from object - (Richard@Bouska.cz) -- Add a little more error checking to the quote function in the wrapper -- Add extra checking in `_quote` function -- Wrap query in pg.py for debugging -- Add DB-API 2.0 support to pgmodule.c (andre@via.ecp.fr) -- Add DB-API 2.0 wrapper pgdb.py (andre@via.ecp.fr) -- Correct keyword clash (temp) in tutorial -- Clean up layout of tutorial -- Return NULL values as None (rlawrence@lastfoot.com) - (WARNING: This will cause backwards compatibility issues) -- Change None to NULL in insert and update -- Change hash-bang lines to use /usr/bin/env -- Clearing date should be blank (NULL) not TODAY -- Quote backslashes in strings in `_quote` (brian@CSUA.Berkeley.EDU) -- Expanded and clarified build instructions (tbryan@starship.python.net) -- Make code thread safe (Jerome.Alet@unice.fr) -- Add README.distutils (mwa@gate.net & jeremy@cnri.reston.va.us) -- Many fixes and increased DB-API compliance by chifungfan@yahoo.com, - tony@printra.net, jeremy@alum.mit.edu and others to get the final - version ready to release. - -Version 2.4 (1999-06-15) ------------------------- -- Insert returns None if the user doesn't have select permissions - on the table. It can (and does) happen that one has insert but - not select permissions on a table. -- Added ntuples() method to query object (brit@druid.net) -- Corrected a bug related to getresult() and the money type -- Corrected a bug related to negative money amounts -- Allow update based on primary key if munged oid not available and - table has a primary key -- Add many __doc__ strings (andre@via.ecp.fr) -- Get method works with views if key specified - -Version 2.3 (1999-04-17) ------------------------- -- connect.host returns "localhost" when connected to Unix socket - (torppa@tuhnu.cutery.fi) -- Use `PyArg_ParseTupleAndKeywords` in connect() (torppa@tuhnu.cutery.fi) -- fixes and cleanups (torppa@tuhnu.cutery.fi) -- Fixed memory leak in dictresult() (terekhov@emc.com) -- Deprecated pgext.py - functionality now in pg.py -- More cleanups to the tutorial -- Added fileno() method - terekhov@emc.com (Mikhail Terekhov) -- added money type to quoting function -- Compiles cleanly with more warnings turned on -- Returns PostgreSQL error message on error -- Init accepts keywords (Jarkko Torppa) -- Convenience functions can be overridden (Jarkko Torppa) -- added close() method - -Version 2.2 (1998-12-21) ------------------------- -- Added user and password support thanks to Ng Pheng Siong (ngps@post1.com) -- Insert queries return the inserted oid -- Add new `pg` wrapper (C module renamed to _pg) -- Wrapped database connection in a class -- Cleaned up some of the tutorial. (More work needed.) -- Added `version` and `__version__`. - Thanks to thilo@eevolute.com for the suggestion. - -Version 2.1 (1998-03-07) ------------------------- -- return fields as proper Python objects for field type -- Cleaned up pgext.py -- Added dictresult method - -Version 2.0 (1997-12-23) -------------------------- -- Updated code for PostgreSQL 6.2.1 and Python 1.5 -- Reformatted code and converted to use full ANSI style prototypes -- Changed name to PyGreSQL (from PyGres95) -- Changed order of arguments to connect function -- Created new type `pgqueryobject` and moved certain methods to it -- Added a print function for pgqueryobject -- Various code changes - mostly stylistic - -Version 1.0b (1995-11-04) -------------------------- -- Keyword support for connect function moved from library file to C code - and taken away from library -- Rewrote documentation -- Bug fix in connect function -- Enhancements in large objects interface methods - -Version 1.0a (1995-10-30) -------------------------- -A limited release. - -- Module adapted to standard Python syntax -- Keyword support for connect function in library file -- Rewrote default parameters interface (internal use of strings) -- Fixed minor bugs in module interface -- Redefinition of error messages - -Version 0.9b (1995-10-10) -------------------------- -The first public release. - -- Large objects implementation -- Many bug fixes, enhancements, ... - -Version 0.1a (1995-10-07) -------------------------- -- Basic libpq functions (SQL access) diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/default.css b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/default.css deleted file mode 100644 index 15d0778af1b1..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/default.css +++ /dev/null @@ -1,279 +0,0 @@ -/* -:Author: David Goodger -:Contact: goodger@users.sourceforge.net -:Date: $Date: 2006/01/22 16:15:33 $ -:Revision: $Revision: 1.2 $ -:Copyright: This stylesheet has been placed in the public domain. - -Default cascading style sheet for the HTML output of Docutils. - -See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to -customize this style sheet. -*/ - -/* used to remove borders from tables and images */ -.borderless, table.borderless td, table.borderless th { - border: 0 } - -table.borderless td, table.borderless th { - /* Override padding for "table.docutils td" with "! important". - The right padding separates the table cells. */ - padding: 0 0.5em 0 0 ! important } - -.first { - /* Override more specific margin styles with "! important". */ - margin-top: 0 ! important } - -.last, .with-subtitle { - margin-bottom: 0 ! important } - -.hidden { - display: none } - -a.toc-backref { - text-decoration: none ; - color: black } - -blockquote.epigraph { - margin: 2em 5em ; } - -dl.docutils dd { - margin-bottom: 0.5em } - -/* Uncomment (and remove this text!) to get bold-faced definition list terms -dl.docutils dt { - font-weight: bold } -*/ - -div.abstract { - margin: 2em 5em } - -div.abstract p.topic-title { - font-weight: bold ; - text-align: center } - -div.admonition, div.attention, div.caution, div.danger, div.error, -div.hint, div.important, div.note, div.tip, div.warning { - margin: 2em ; - border: medium outset ; - padding: 1em } - -div.admonition p.admonition-title, div.hint p.admonition-title, -div.important p.admonition-title, div.note p.admonition-title, -div.tip p.admonition-title { - font-weight: bold ; - font-family: sans-serif } - -div.attention p.admonition-title, div.caution p.admonition-title, -div.danger p.admonition-title, div.error p.admonition-title, -div.warning p.admonition-title { - color: red ; - font-weight: bold ; - font-family: sans-serif } - -/* Uncomment (and remove this text!) to get reduced vertical space in - compound paragraphs. -div.compound .compound-first, div.compound .compound-middle { - margin-bottom: 0.5em } - -div.compound .compound-last, div.compound .compound-middle { - margin-top: 0.5em } -*/ - -div.dedication { - margin: 2em 5em ; - text-align: center ; - font-style: italic } - -div.dedication p.topic-title { - font-weight: bold ; - font-style: normal } - -div.figure { - margin-left: 2em ; - margin-right: 2em } - -div.footer, div.header { - clear: both; - font-size: smaller } - -div.line-block { - display: block ; - margin-top: 1em ; - margin-bottom: 1em } - -div.line-block div.line-block { - margin-top: 0 ; - margin-bottom: 0 ; - margin-left: 1.5em } - -div.sidebar { - margin-left: 1em ; - border: medium outset ; - padding: 1em ; - background-color: #ffffee ; - width: 40% ; - float: right ; - clear: right } - -div.sidebar p.rubric { - font-family: sans-serif ; - font-size: medium } - -div.system-messages { - margin: 5em } - -div.system-messages h1 { - color: red } - -div.system-message { - border: medium outset ; - padding: 1em } - -div.system-message p.system-message-title { - color: red ; - font-weight: bold } - -div.topic { - margin: 2em } - -h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, -h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { - margin-top: 0.4em } - -h1.title { - text-align: center } - -h2.subtitle { - text-align: center } - -hr.docutils { - width: 75% } - -img.align-left { - clear: left } - -img.align-right { - clear: right } - -ol.simple, ul.simple { - margin-bottom: 1em } - -ol.arabic { - list-style: decimal } - -ol.loweralpha { - list-style: lower-alpha } - -ol.upperalpha { - list-style: upper-alpha } - -ol.lowerroman { - list-style: lower-roman } - -ol.upperroman { - list-style: upper-roman } - -p.attribution { - text-align: right ; - margin-left: 50% } - -p.caption { - font-style: italic } - -p.credits { - font-style: italic ; - font-size: smaller } - -p.label { - white-space: nowrap } - -p.rubric { - font-weight: bold ; - font-size: larger ; - color: maroon ; - text-align: center } - -p.sidebar-title { - font-family: sans-serif ; - font-weight: bold ; - font-size: larger } - -p.sidebar-subtitle { - font-family: sans-serif ; - font-weight: bold } - -p.topic-title { - font-weight: bold } - -pre.address { - margin-bottom: 0 ; - margin-top: 0 ; - font-family: serif ; - font-size: 100% } - -pre.literal-block, pre.doctest-block { - margin-left: 2em ; - margin-right: 2em ; - background-color: #eeeeee } - -span.classifier { - font-family: sans-serif ; - font-style: oblique } - -span.classifier-delimiter { - font-family: sans-serif ; - font-weight: bold } - -span.interpreted { - font-family: sans-serif } - -span.option { - white-space: nowrap } - -span.pre { - white-space: pre } - -span.problematic { - color: red } - -span.section-subtitle { - /* font-size relative to parent (h1..h6 element) */ - font-size: 80% } - -table.citation { - border-left: solid 1px gray; - margin-left: 1px } - -table.docinfo { - margin: 2em 4em } - -table.docutils { - margin-top: 0.5em ; - margin-bottom: 0.5em } - -table.footnote { - border-left: solid 1px black; - margin-left: 1px } - -table.docutils td, table.docutils th, -table.docinfo td, table.docinfo th { - padding-left: 0.5em ; - padding-right: 0.5em ; - vertical-align: top } - -table.docutils th.field-name, table.docinfo th.docinfo-name { - font-weight: bold ; - text-align: left ; - white-space: nowrap ; - padding-left: 0 } - -h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, -h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { - font-size: 100% } - -tt.docutils { - background-color: #eeeeee } - -ul.auto-toc { - list-style-type: none } diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/docs.css b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/docs.css deleted file mode 100644 index 3d99c950368d..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/docs.css +++ /dev/null @@ -1,109 +0,0 @@ -/* -Stylesheet for use with Docutils. - -Customized for PyGreSQL docs. -*/ - -@import url(default.css); - -body { - margin: 8pt; - padding: 8pt; - background-color: #f8f8ff; - color: #000008; - text-align: justify; - font-family: Arial, Verdana, Helvetica, sans-serif; - font-size: 11pt; } - -a { - text-decoration: none; } - -a:hover { - text-decoration: underline; } - -.title, .subtitle { - color: #003; } - -.topic-title { - color: #006; - font-size: 14pt; } - -h1, h2, h3, h4 { - color: #006; } - -h1 { - padding-top: 20pt; - font-size: 17pt; } - -div#pygresql-changelog div.section h1 { - font-size: 12pt; -} - -h1.title { - font-size: 20pt; -} - -h2 { - font-size: 14pt; } - -h2.subtitle { - font-size: 16pt; -} - -h3 { - font-size: 13pt; } - -h4 { - font-size: 12pt; } - -a.toc-backref { - color: #006; } - -ul.simple li { - margin-top: 4pt; } - -ul.simple ul li { - margin-top: 2pt; } - -div.contents ul { - list-style-type: none; } - -div.contents ul li { - margin-top: 4pt; - font-size: 12pt; } - -div.contents ul ul li { - margin-top: 2pt; - font-size: 11pt; } - -cite { - font-style: normal; - font-family: monospace; - font-weight: bold; } - -table.field-list th.field-name { - font-style: normal; - font-family: monospace; - font-weight: bold; } - -tt.literal, pre.literal-block { - font-style: normal; - font-family: monospace; - font-weight: bold; - background-color: #fff; } - -tt.literal { - padding-left: 2pt; - padding-right: 2pt; } - -pre.literal-block { - padding: 4pt; - border: 1px dotted #ccc; } - -table.docutils { - border-spacing: 0px; - border-collapse: collapse; } - -table.docutils td { - margin: 0px; - padding: 2pt; } diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/future.html b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/future.html deleted file mode 100644 index 358de81ea4c5..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/future.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - -PyGreSQL future directions - - - -
-

PyGreSQL future directions

-
-

To Do

-
    -
  • Documentation for the pgdb module (everything specific to PyGreSQL).
  • -
  • The large object and direct access functions need much more attention.
  • -
  • The C module needs to be cleaned up and redundant code merged, -and should get its own unit test module.
  • -
  • The fetch method should use real cursors.
  • -
  • What shall we do with the "tutorial" directory -(it's rather a tutorial for Postgres/SQL than for PyGreSQL, -it's using only the query method from the classic pg module and -no other PyGreSQL functionality, it's rather a demo than a tutorial)?
  • -
-
-
-

Proposed Patches

-
    -
  • Notice handling with PQsetNoticeReceiver and PQsetNoticeProcessor -(one possible implementation was already suggested by Dmitry Dvoinikov -http://mailman.vex.net/pipermail/pygresql/2005-November/001530.html). -Maybe also make notifications accessible via the optional cursor and -connection attribute "messages" proposed in the DB-API specs.
  • -
-
-
-

Wish List

-
    -
  • Make SQLSTATE error codes available.
  • -
  • Make use of PQexecParams() and PQprepare(). This could speed up -executemany() and allow retrieving binary data directly by setting -the resultFormat parameter to one.
  • -
  • Support optional "errorhandler" extension.
  • -
  • Support optional cursor and connection attribute "messages".
  • -
  • Connection as context manager (see http://tinyurl.com/6j9cef).
  • -
  • Users should be able to register their own types with _pg.
  • -
  • Let pg and pgdb support namedtuples (as available in Py 2.6). -pg could get a new method namedresult(), and pgdb could provide -a row factory for namedtuples (similar to sqlite3).
  • -
  • New methods in the classic module, similar to getresult() and -dictresult(), but returning dictionaries of rows instead of lists -of rows (with primary key or oids as keys).
  • -
  • Make PyGreSQL thread-safe on the connection level.
  • -
  • The API documentation could be created with Epydoc.
  • -
  • Write a tutorial for beginners and advanced use.
  • -
  • More and better documented examples.
  • -
-
-
- - diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/future.txt b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/future.txt deleted file mode 100644 index 8dec0fb8d4c6..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/future.txt +++ /dev/null @@ -1,48 +0,0 @@ -========================== -PyGreSQL future directions -========================== - - -To Do ------ - -- Documentation for the pgdb module (everything specific to PyGreSQL). -- The large object and direct access functions need much more attention. -- The C module needs to be cleaned up and redundant code merged, - and should get its own unit test module. -- The fetch method should use real cursors. -- What shall we do with the "tutorial" directory - (it's rather a tutorial for Postgres/SQL than for PyGreSQL, - it's using only the query method from the classic pg module and - no other PyGreSQL functionality, it's rather a demo than a tutorial)? - -Proposed Patches ----------------- - -- Notice handling with PQsetNoticeReceiver and PQsetNoticeProcessor - (one possible implementation was already suggested by Dmitry Dvoinikov - http://mailman.vex.net/pipermail/pygresql/2005-November/001530.html). - Maybe also make notifications accessible via the optional cursor and - connection attribute "messages" proposed in the DB-API specs. - -Wish List ---------- - -- Make SQLSTATE error codes available. -- Make use of PQexecParams() and PQprepare(). This could speed up - executemany() and allow retrieving binary data directly by setting - the resultFormat parameter to one. -- Support optional "errorhandler" extension. -- Support optional cursor and connection attribute "messages". -- Connection as context manager (see http://tinyurl.com/6j9cef). -- Users should be able to register their own types with _pg. -- Let pg and pgdb support namedtuples (as available in Py 2.6). - pg could get a new method namedresult(), and pgdb could provide - a row factory for namedtuples (similar to sqlite3). -- New methods in the classic module, similar to getresult() and - dictresult(), but returning dictionaries of rows instead of lists - of rows (with primary key or oids as keys). -- Make PyGreSQL thread-safe on the connection level. -- The API documentation could be created with Epydoc. -- Write a tutorial for beginners and advanced use. -- More and better documented examples. diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/index.html b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/index.html deleted file mode 100644 index be2b37cd3e6e..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/index.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - PyGreSQL - PostgreSQL module for Python - - - - - - - - -
PyGreSQL
-
Version 4.0
- -
:: PostgreSQL module for Python ::
- -
- -

PyGreSQL – PostgreSQL module for Python

- -

PyGreSQL is an open-source - Python module - that interfaces to a PostgreSQL database. - It embeds the PostgreSQL query library to allow easy use of the powerful PostgreSQL - features from a Python script.

- -

This software is copyright © 1995, Pascal Andre.
- Further modifications are copyright © 1997-2006 by D'Arcy J.M. Cain.

- -

See the - copyright notice - for detailed information.

- - -

Documentation

- -

The following information is also available in the docs folder of the distribution:

- - - - -

CVS Access

- -

The - CVS repository - is available through - CVSWeb.

- - -

Mailing list

- -

You can join - the mailing - list to discuss future development of the PyGreSQL interface. - This is usually a low volume list except when there are new features - being added.

- - -

Examples

- -

I am starting to collect examples of applications that use PyGreSQL. - So far I only have a few but if you have an example for me, you can - either send me the files or the URL for me to point to.

- -

Here is a List of motorcycle - rides in Ontario that uses a PostgreSQL database to store the - rides. There is a link at the bottom of the page to view the source code.

- -

- Oleg Broytmann has written a simple example - RGB - database demo. -

- -
- - - - - diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/install.html b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/install.html deleted file mode 100644 index f1e48c03f877..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/install.html +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - -PyGreSQL Installation - - - -
-

PyGreSQL Installation

- -
-

1   General

-

You must first have installed Python and PostgreSQL on your system. -If you want to access remote database only, you don't need to install -the full PostgreSQL server, but only the C interface (libpq). If you -are on Windows, make sure that the directory with libpq.dll is in your -PATH environment variable.

-

The current version of PyGreSQL has been tested with Python 2.5 and -PostGreSQL 8.3. Older version should work as well, but you will need -at least Python 2.3 and PostgreSQL 7.4.

-

PyGreSQL will be installed as three modules, a dynamic module called -_pg.pyd, and two pure Python wrapper modules called pg.py and pgdb.py. -All three files will be installed directly into the Python site-packages -directory. To uninstall PyGreSQL, simply remove these three files again.

-
-
-

2   Installing from a Binary Distribution

-

This is the easiest way to install PyGreSQL.

-

You can currently download PyGreSQL as Linux RPM, NetBSD package and Windows -installer. Make sure the required Python version of the binary package matches -the Python version you have installed.

-

Install the package as usual on your system.

-

Note that the documentation is currently only included in the source package.

-
-
-

3   Installing from Source

-

If you want to install PyGreSQL from Source, or there is no binary -package available for your platform, follow these instructions.

-

Make sure the Python header files and PostgreSQL client and server header -files are installed. These come usually with the "devel" packages on Unix -systems and the installer executables on Windows systems.

-

If you are using a precompiled PostgreSQL, you will also need the pg_config -tool. This is usually also part of the "devel" package on Unix, and will be -installed as part of the database server feature on Windows systems.

-
-

3.1   Building and installing with Distutils

-

You can build and install PyGreSQL using -Distutils.

-

Download and unpack the PyGreSQL source tarball if you haven't already done so.

-

Type the following commands to build and install PyGreSQL:

-
-python setup.py build
-python setup.py install
-
-

If you are using MinGW to build PyGreSQL under -Microsoft Windows, please note that Python newer version 2.3 is using msvcr71 -instead of msvcrt as its common runtime library. You can allow for that by -editing the file %MinGWpath%/lib/gcc/%MinGWversion%/specs and changing -the entry that reads -lmsvcrt to -lmsvcr71. You may also need to copy -libpq.lib to libpq.a in the PostgreSQL lib directory. Then use -the following command to build and install PyGreSQL:

-
-python setup.py build -c mingw32 install
-
-

Now you should be ready to use PyGreSQL.

-
-
-

3.2   Compiling Manually

-

The source file for compiling the dynamic module is called pgmodule.c. -You have two options. You can compile PyGreSQL as a stand-alone module -or you can build it into the Python interpreter.

-
-
-

3.3   Stand-Alone

-
    -
  • In the directory containing pgmodule.c, run the following command:

    -
    -cc -fpic -shared -o _pg.so -I$PYINC -I$PGINC -I$PSINC -L$PGLIB -lpq pgmodule.c
    -
    -

    where you have to set:

    -
    -PYINC = path to the Python include files
    -        (usually something like /usr/include/python)
    -PGINC = path to the PostgreSQL client include files
    -        (something like /usr/include/pgsql or /usr/include/postgresql)
    -PSINC = path to the PostgreSQL server include files
    -        (like /usr/include/pgsql/server or /usr/include/postgresql/server)
    -PGLIB = path to the PostgreSQL object code libraries (usually /usr/lib)
    -
    -

    If you are not sure about the above paths, try something like:

    -
    -PYINC=`find /usr -name Python.h`
    -PGINC=`find /usr -name libpq-fe.h`
    -PSINC=`find /usr -name postgres.h`
    -PGLIB=`find /usr -name libpq.so`
    -
    -

    If you have the pg_config tool installed, you can set:

    -
    -PGINC=`pg_config --includedir`
    -PSINC=`pg_config --includedir-server`
    -PGLIB=`pg_config --libdir`
    -
    -

    Some options may be added to this line:

    -
    --DNO_DEF_VAR   no default variables support
    --DNO_DIRECT    no direct access methods
    --DNO_LARGE     no large object support
    --DNO_PQSOCKET  if running an older PostgreSQL
    -
    -

    Define NO_PQSOCKET if you are using a version of PostgreSQL before 6.4 -that does not have the PQsocket function. The other options will be -described in the next sections.

    -

    On some systems you may need to include -lcrypt in the list of libraries -to make it compile.

    -
  • -
  • Test the new module. Something like the following should work:

    -
    -$ python
    -
    ->>> import _pg
    ->>> db = _pg.connect('thilo','localhost')
    ->>> db.query("INSERT INTO test VALUES ('ping','pong')")
    -18304
    ->>> db.query("SELECT * FROM test")
    -eins|zwei
    -----+----
    -ping|pong
    -(1 row)
    -
    -
  • -
  • Finally, move the _pg.so, pg.py, and pgdb.py to a directory in -your PYTHONPATH. A good place would be /usr/lib/python/site-packages -if your Python modules are in /usr/lib/python.

    -
  • -
-
-
-

3.4   Built-in to Python interpreter

-
    -
  • Find the directory where your Setup file lives (usually in the Modules -subdirectory) in the Python source hierarchy and copy or symlink the -pgmodule.c file there.

    -
  • -
  • Add the following line to your 'Setup' file:

    -
    -_pg  pgmodule.c -I$PGINC -I$PSINC -L$PGLIB -lpq
    -
    -

    where:

    -
    -PGINC = path to the PostgreSQL client include files (see above)
    -PSINC = path to the PostgreSQL server include files (see above)
    -PGLIB = path to the PostgreSQL object code libraries (see above)
    -
    -

    Some options may be added to this line:

    -
    --DNO_DEF_VAR   no default variables support
    --DNO_DIRECT    no direct access methods
    --DNO_LARGE     no large object support
    --DNO_PQSOCKET  if running an older PostgreSQL (see above)
    -
    -

    On some systems you may need to include -lcrypt in the list of libraries -to make it compile.

    -
  • -
  • If you want a shared module, make sure that the shared keyword is -uncommented and add the above line below it. You used to need to install -your shared modules with make sharedinstall but this no longer seems -to be true.

    -
  • -
  • Copy pg.py to the lib directory where the rest of your modules are. -For example, that's /usr/local/lib/Python on my system.

    -
  • -
  • Rebuild Python from the root directory of the Python source hierarchy by -running make -f Makefile.pre.in boot and make && make install.

    -
  • -
  • For more details read the documentation at the top of Makefile.pre.in.

    -
  • -
-
-
-
- - diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/install.txt b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/install.txt deleted file mode 100644 index 3f19a2c5a29e..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/install.txt +++ /dev/null @@ -1,188 +0,0 @@ -===================== -PyGreSQL Installation -===================== - -.. sectnum:: -.. contents:: Contents - - -General -======= - -You must first have installed Python and PostgreSQL on your system. -If you want to access remote database only, you don't need to install -the full PostgreSQL server, but only the C interface (libpq). If you -are on Windows, make sure that the directory with libpq.dll is in your -``PATH`` environment variable. - -The current version of PyGreSQL has been tested with Python 2.5 and -PostGreSQL 8.3. Older version should work as well, but you will need -at least Python 2.3 and PostgreSQL 7.4. - -PyGreSQL will be installed as three modules, a dynamic module called -_pg.pyd, and two pure Python wrapper modules called pg.py and pgdb.py. -All three files will be installed directly into the Python site-packages -directory. To uninstall PyGreSQL, simply remove these three files again. - - -Installing from a Binary Distribution -===================================== - -This is the easiest way to install PyGreSQL. - -You can currently download PyGreSQL as Linux RPM, NetBSD package and Windows -installer. Make sure the required Python version of the binary package matches -the Python version you have installed. - -Install the package as usual on your system. - -Note that the documentation is currently only included in the source package. - - -Installing from Source -====================== - -If you want to install PyGreSQL from Source, or there is no binary -package available for your platform, follow these instructions. - -Make sure the Python header files and PostgreSQL client and server header -files are installed. These come usually with the "devel" packages on Unix -systems and the installer executables on Windows systems. - -If you are using a precompiled PostgreSQL, you will also need the pg_config -tool. This is usually also part of the "devel" package on Unix, and will be -installed as part of the database server feature on Windows systems. - -Building and installing with Distutils --------------------------------------- - -You can build and install PyGreSQL using -`Distutils `_. - -Download and unpack the PyGreSQL source tarball if you haven't already done so. - -Type the following commands to build and install PyGreSQL:: - - python setup.py build - python setup.py install - -If you are using `MinGW `_ to build PyGreSQL under -Microsoft Windows, please note that Python newer version 2.3 is using msvcr71 -instead of msvcrt as its common runtime library. You can allow for that by -editing the file ``%MinGWpath%/lib/gcc/%MinGWversion%/specs`` and changing -the entry that reads ``-lmsvcrt`` to ``-lmsvcr71``. You may also need to copy -``libpq.lib`` to ``libpq.a`` in the PostgreSQL ``lib`` directory. Then use -the following command to build and install PyGreSQL:: - - python setup.py build -c mingw32 install - -Now you should be ready to use PyGreSQL. - -Compiling Manually ------------------- - -The source file for compiling the dynamic module is called pgmodule.c. -You have two options. You can compile PyGreSQL as a stand-alone module -or you can build it into the Python interpreter. - -Stand-Alone ------------ - -* In the directory containing ``pgmodule.c``, run the following command:: - - cc -fpic -shared -o _pg.so -I$PYINC -I$PGINC -I$PSINC -L$PGLIB -lpq pgmodule.c - - where you have to set:: - - PYINC = path to the Python include files - (usually something like /usr/include/python) - PGINC = path to the PostgreSQL client include files - (something like /usr/include/pgsql or /usr/include/postgresql) - PSINC = path to the PostgreSQL server include files - (like /usr/include/pgsql/server or /usr/include/postgresql/server) - PGLIB = path to the PostgreSQL object code libraries (usually /usr/lib) - - If you are not sure about the above paths, try something like:: - - PYINC=`find /usr -name Python.h` - PGINC=`find /usr -name libpq-fe.h` - PSINC=`find /usr -name postgres.h` - PGLIB=`find /usr -name libpq.so` - - If you have the ``pg_config`` tool installed, you can set:: - - PGINC=`pg_config --includedir` - PSINC=`pg_config --includedir-server` - PGLIB=`pg_config --libdir` - - Some options may be added to this line:: - - -DNO_DEF_VAR no default variables support - -DNO_DIRECT no direct access methods - -DNO_LARGE no large object support - -DNO_PQSOCKET if running an older PostgreSQL - - Define ``NO_PQSOCKET`` if you are using a version of PostgreSQL before 6.4 - that does not have the PQsocket function. The other options will be - described in the next sections. - - On some systems you may need to include ``-lcrypt`` in the list of libraries - to make it compile. - -* Test the new module. Something like the following should work:: - - $ python - - >>> import _pg - >>> db = _pg.connect('thilo','localhost') - >>> db.query("INSERT INTO test VALUES ('ping','pong')") - 18304 - >>> db.query("SELECT * FROM test") - eins|zwei - ----+---- - ping|pong - (1 row) - -* Finally, move the ``_pg.so``, ``pg.py``, and ``pgdb.py`` to a directory in - your ``PYTHONPATH``. A good place would be ``/usr/lib/python/site-packages`` - if your Python modules are in ``/usr/lib/python``. - -Built-in to Python interpreter ------------------------------- - -* Find the directory where your ``Setup`` file lives (usually in the ``Modules`` - subdirectory) in the Python source hierarchy and copy or symlink the - ``pgmodule.c`` file there. - -* Add the following line to your 'Setup' file:: - - _pg pgmodule.c -I$PGINC -I$PSINC -L$PGLIB -lpq - - where:: - - PGINC = path to the PostgreSQL client include files (see above) - PSINC = path to the PostgreSQL server include files (see above) - PGLIB = path to the PostgreSQL object code libraries (see above) - - Some options may be added to this line:: - - -DNO_DEF_VAR no default variables support - -DNO_DIRECT no direct access methods - -DNO_LARGE no large object support - -DNO_PQSOCKET if running an older PostgreSQL (see above) - - On some systems you may need to include ``-lcrypt`` in the list of libraries - to make it compile. - -* If you want a shared module, make sure that the ``shared`` keyword is - uncommented and add the above line below it. You used to need to install - your shared modules with ``make sharedinstall`` but this no longer seems - to be true. - -* Copy ``pg.py`` to the lib directory where the rest of your modules are. - For example, that's ``/usr/local/lib/Python`` on my system. - -* Rebuild Python from the root directory of the Python source hierarchy by - running ``make -f Makefile.pre.in boot`` and ``make && make install``. - -* For more details read the documentation at the top of ``Makefile.pre.in``. diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pg.html b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pg.html deleted file mode 100644 index 2e903789a758..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pg.html +++ /dev/null @@ -1,2429 +0,0 @@ - - - - - - -PyGreSQL Programming Information - - - - - -
-

PyGreSQL Programming Information

-

The classic PyGreSQL interface (pg module)

-
-

Contents

- -
-
-

1   Introduction

-

You may either choose to use the -"classic" PyGreSQL interface -provided by the pg module or else the -DB-API 2.0 compliant interface -provided by the pgdb module.

-

The following documentation covers only the older pg API.

-

The pg module handles three types of objects,

-
    -
  • the pgobject, which handles the connection -and all the requests to the database,
  • -
  • the pglarge object, which handles -all the accesses to PostgreSQL large objects,
  • -
  • the pgqueryobject that handles query results
  • -
-

and it provides a convenient wrapper class DB for the pgobject.

-

If you want to see a simple example of the use of some of these functions, -see http://ontario.bikerides.ca where you can find a link at the bottom to the -actual Python code for the page.

-
-
-

2   Module functions and constants

-

The pg module defines a few functions that allow to connect -to a database and to define "default variables" that override -the environment variables used by PostgreSQL.

-

These "default variables" were designed to allow you to handle general -connection parameters without heavy code in your programs. You can prompt the -user for a value, put it in the default variable, and forget it, without -having to modify your environment. The support for default variables can be -disabled by setting the -DNO_DEF_VAR option in the Python setup file. Methods -relative to this are specified by the tag [DV].

-

All variables are set to None at module initialization, specifying that -standard environment variables should be used.

-
-

2.1   connect - opens a pg connection

-

Syntax:

-
-connect([dbname], [host], [port], [opt], [tty], [user], [passwd])
-
-
-
Parameters:
-
--- - - - - - - - - - - - - - - - -
dbname:name of connected database (string/None)
host:name of the server host (string/None)
port:port used by the database server (integer/-1)
opt:connection options (string/None)
tty:debug terminal (string/None)
user:PostgreSQL user (string/None)
passwd:password for user (string/None)
-
-
Return type:
-
--- - - - -
pgobject:If successful, the pgobject handling the connection
-
-
Exceptions raised:
-
--- - - - - - - - - -
TypeError:bad argument type, or too many arguments
SyntaxError:duplicate argument definition
pg.InternalError:
 some error occurred during pg connection definition
-

(plus all exceptions relative to object allocation)

-
-
Description:
-
This function opens a connection to a specified database on a given -PostgreSQL server. You can use keywords here, as described in the -Python tutorial. The names of the keywords are the name of the -parameters given in the syntax line. For a precise description -of the parameters, please refer to the PostgreSQL user manual.
-
-

Examples:

-
-import pg
-
-con1 = pg.connect('testdb', 'myhost', 5432, None, None, 'bob', None)
-con2 = pg.connect(dbname='testdb', host='localhost', user='bob')
-
-
-
-

2.2   get_defhost, set_defhost - default server host [DV]

-

Syntax:

-
-get_defhost()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
string, None:default host specification
-
-
Exceptions raised:
-
--- - - - -
TypeError:too many arguments
-
-
Description:
-
This method returns the current default host specification, -or None if the environment variables should be used. -Environment variables won't be looked up.
-
-

Syntax:

-
-set_defhost(host)
-
-
-
Parameters:
-
--- - - - -
host:new default host (string/None)
-
-
Return type:
-
--- - - - -
string, None:previous default host specification
-
-
Exceptions raised:
-
--- - - - -
TypeError:bad argument type, or too many arguments
-
-
Description:
-
This methods sets the default host value for new connections. -If None is supplied as parameter, environment variables will -be used in future connections. It returns the previous setting -for default host.
-
-
-
-

2.3   get_defport, set_defport - default server port [DV]

-

Syntax:

-
-get_defport()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
integer, None:default port specification
-
-
Exceptions raised:
-
--- - - - -
TypeError:too many arguments
-
-
Description:
-
This method returns the current default port specification, -or None if the environment variables should be used. -Environment variables won't be looked up.
-
-

Syntax:

-
-set_defport(port)
-
-
-
Parameters:
-
--- - - - -
port:new default port (integer/-1)
-
-
Return type:
-
--- - - - -
integer, None:previous default port specification
-
-
Description:
-
This methods sets the default port value for new connections. If -1 is -supplied as parameter, environment variables will be used in future -connections. It returns the previous setting for default port.
-
-
-
-

2.4   get_defopt, set_defopt - default connection options [DV]

-

Syntax:

-
-get_defopt()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
string, None:default options specification
-
-
Exceptions raised:
-
--- - - - -
TypeError:too many arguments
-
-
Description:
-
This method returns the current default connection options specification, -or None if the environment variables should be used. Environment variables -won't be looked up.
-
-

Syntax:

-
-set_defopt(options)
-
-
-
Parameters:
-
--- - - - -
options:new default connection options (string/None)
-
-
Return type:
-
--- - - - -
string, None:previous default options specification
-
-
Exceptions raised:
-
--- - - - -
TypeError:bad argument type, or too many arguments
-
-
Description:
-
This methods sets the default connection options value for new connections. -If None is supplied as parameter, environment variables will be used in -future connections. It returns the previous setting for default options.
-
-
-
-

2.5   get_deftty, set_deftty - default debug tty [DV]

-

Syntax:

-
-get_deftty()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
string, None:default debug terminal specification
-
-
Exceptions raised:
-
--- - - - -
TypeError:too many arguments
-
-
Description:
-
This method returns the current default debug terminal specification, or -None if the environment variables should be used. Environment variables -won't be looked up.
-
-

Syntax:

-
-set_deftty(terminal)
-
-
-
Parameters:
-
--- - - - -
terminal:new default debug terminal (string/None)
-
-
Return type:
-
--- - - - -
string, None:previous default debug terminal specification
-
-
Exceptions raised:
-
--- - - - -
TypeError:bad argument type, or too many arguments
-
-
Description:
-
This methods sets the default debug terminal value for new connections. If -None is supplied as parameter, environment variables will be used in future -connections. It returns the previous setting for default terminal.
-
-
-
-

2.6   get_defbase, set_defbase - default database name [DV]

-

Syntax:

-
-get_defbase()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
string, None:default database name specification
-
-
Exceptions raised:
-
--- - - - -
TypeError:too many arguments
-
-
Description:
-
This method returns the current default database name specification, or -None if the environment variables should be used. Environment variables -won't be looked up.
-
-

Syntax:

-
-set_defbase(base)
-
-
-
Parameters:
-
--- - - - -
base:new default base name (string/None)
-
-
Return type:
-
--- - - - -
string, None:previous default database name specification
-
-
Exceptions raised:
-
--- - - - -
TypeError:bad argument type, or too many arguments
-
-
Description:
-
This method sets the default database name value for new connections. If -None is supplied as parameter, environment variables will be used in -future connections. It returns the previous setting for default host.
-
-
-
-

2.7   escape_string - escape a string for use within SQL

-

Syntax:

-
-escape_string(string)
-
-
-
Parameters:
-
--- - - - -
string:the string that is to be escaped
-
-
Return type:
-
--- - - - -
str:the escaped string
-
-
Exceptions raised:
-
--- - - - -
TypeError:bad argument type, or too many arguments
-
-
Description:
-
This function escapes a string for use within an SQL command. -This is useful when inserting data values as literal constants -in SQL commands. Certain characters (such as quotes and backslashes) -must be escaped to prevent them from being interpreted specially -by the SQL parser. escape_string performs this operation. -Note that there is also a pgobject method with the same name -which takes connection properties into account.
-
-
-

Caution!

-

It is especially important to do proper escaping when -handling strings that were received from an untrustworthy source. -Otherwise there is a security risk: you are vulnerable to "SQL injection" -attacks wherein unwanted SQL commands are fed to your database.

-
-

Example:

-
-name = raw_input("Name? ")
-phone = con.query("select phone from employees"
-  " where name='%s'" % escape_string(name)).getresult()
-
-
-
-

2.8   escape_bytea - escape binary data for use within SQL as type bytea

-

Syntax:

-
-escape_bytea(datastring)
-
-
-
Parameters:
-
--- - - - -
datastring:string containing the binary data that is to be escaped
-
-
Return type:
-
--- - - - -
str:the escaped string
-
-
Exceptions raised:
-
--- - - - -
TypeError:bad argument type, or too many arguments
-
-
Description:
-
Escapes binary data for use within an SQL command with the type bytea. -As with escape_string, this is only used when inserting data directly -into an SQL command string. -Note that there is also a pgobject method with the same name -which takes connection properties into account.
-
-

Example:

-
-picture = file('garfield.gif', 'rb').read()
-con.query("update pictures set img='%s' where name='Garfield'"
-  % escape_bytea(picture))
-
-
-
-

2.9   unescape_bytea -- unescape bytea data that has been retrieved as text

-

Syntax:

-
-unescape_bytea(string)
-
-
-
Parameters:
-
--- - - - -
datastring:the bytea data string that has been retrieved as text
-
-
Return type:
-
--- - - - -
str:string containing the binary data
-
-
Exceptions raised:
-
--- - - - -
TypeError:bad argument type, or too many arguments
-
-
Description:
-
Converts an escaped string representation of binary data into binary -data - the reverse of escape_bytea. This is needed when retrieving -bytea data with the getresult() or dictresult() method.
-
-

Example:

-
-picture = unescape_bytea(con.query(
-  "select img from pictures where name='Garfield'").getresult[0][0])
-file('garfield.gif', 'wb').write(picture)
-
-
-
-

2.10   set_decimal -- set a decimal type to be used for numeric values

-

Syntax:

-
-set_decimal(cls)
-
-
-
Parameters:
-
--- - - - -
cls:the Python class to be used for PostgreSQL numeric values
-
-
Description:
-
This function can be used to specify the Python class that shall be -used by PyGreSQL to hold PostgreSQL numeric values. The default class -is decimal.Decimal if available, otherwise the float type is used.
-
-
-
-

2.11   Module constants

-

Some constants are defined in the module dictionary. -They are intended to be used as parameters for methods calls. -You should refer to the libpq description in the PostgreSQL user manual -for more information about them. These constants are:

- --- - - - - - - - - - - -
version, __version__:
 constants that give the current version.
INV_READ, INV_WRITE:
 large objects access modes, -used by (pgobject.)locreate and (pglarge.)open
SEEK_SET, SEEK_CUR, SEEK_END:
 positional flags, -used by (pglarge.)seek
-
-
-
-

3   Connection objects: pgobject

-

This object handles a connection to a PostgreSQL database. It embeds and -hides all the parameters that define this connection, thus just leaving really -significant parameters in function calls.

-
-

Caution!

-

Some methods give direct access to the connection socket. -Do not use them unless you really know what you are doing. -If you prefer disabling them, -set the -DNO_DIRECT option in the Python setup file.

-

These methods are specified by the tag [DA].

-
-
-

Note

-

Some other methods give access to large objects -(refer to PostgreSQL user manual for more information about these). -If you want to forbid access to these from the module, -set the -DNO_LARGE option in the Python setup file.

-

These methods are specified by the tag [LO].

-
-
-

3.1   query - executes a SQL command string

-

Syntax:

-
-query(command)
-
-
-
Parameters:
-
--- - - - -
command:SQL command (string)
-
-
Return type:
-
--- - - - - -
pgqueryobject, None:
 result values
-
-
Exceptions raised:
-
--- - - - - - - - - - - - -
TypeError:bad argument type, or too many arguments
ValueError:empty SQL query or lost connection
pg.ProgrammingError:
 error in query
pg.InternalError':
 error during query processing
-
-
Description:
-
This method simply sends a SQL query to the database. If the query is an -insert statement that inserted exactly one row into a table that has OIDs, the -return value is the OID of the newly inserted row. If the query is an update -or delete statement, or an insert statement that did not insert exactly one -row in a table with OIDs, then the numer of rows affected is returned as a -string. If it is a statement that returns rows as a result (usually a select -statement, but maybe also an "insert/update ... returning" statement), this -method returns a pgqueryobject that can be accessed via the getresult() -or dictresult() method or simply printed. Otherwise, it returns None.
-
-
-
-

3.2   reset - resets the connection

-

Syntax:

-
-reset()
-
-
-
Parameters:
-
None
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - -
TypeError:too many (any) arguments
-
-
Description:
-
This method resets the current database connection.
-
-
-
-

3.3   cancel - abandon processing of current SQL command

-

Syntax:

-
-cancel()
-
-
-
Parameters:
-
None
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - -
TypeError:too many (any) arguments
-
-
Description:
-
This method requests that the server abandon processing -of the current SQL command.
-
-
-
-

3.4   close - close the database connection

-

Syntax:

-
-close()
-
-
-
Parameters:
-
None
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - -
TypeError:too many (any) arguments
-
-
Description:
-
This method closes the database connection. The connection will -be closed in any case when the connection is deleted but this -allows you to explicitly close it. It is mainly here to allow -the DB-SIG API wrapper to implement a close function.
-
-
-
-

3.5   fileno - returns the socket used to connect to the database

-

Syntax:

-
-fileno()
-
-
-
Parameters:
-
None
-
Exceptions raised:
-
--- - - - -
TypeError:too many (any) arguments
-
-
Description:
-
This method returns the underlying socket id used to connect -to the database. This is useful for use in select calls, etc.
-
-
-
-

3.6   getnotify - gets the last notify from the server

-

Syntax:

-
-getnotify()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
tuple, None:last notify from server
-
-
Exceptions raised:
-
--- - - - - - -
TypeError:too many parameters
TypeError:invalid connection
-
-
Description:
-
This methods try to get a notify from the server (from the SQL statement -NOTIFY). If the server returns no notify, the methods returns None. -Otherwise, it returns a tuple (couple) (relname, pid), where relname -is the name of the notify and pid the process id of the connection that -triggered the notify. Remember to do a listen query first otherwise -getnotify() will always return None.
-
-
-
-

3.7   inserttable - insert a list into a table

-

Syntax:

-
-inserttable(table, values)
-
-
-
Parameters:
-
--- - - - - - -
table:the table name (string)
values:list of rows values (list)
-
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - - - - - -
TypeError:invalid connection, bad argument type, or too many arguments
MemoryError:insert buffer could not be allocated
ValueError:unsupported values
-
-
Description:
-
This method allow to quickly insert large blocks of data in a table: -It inserts the whole values list into the given table. Internally, it -uses the COPY command of the PostgreSQL database. The list is a list -of tuples/lists that define the values for each inserted row. The rows -values may contain string, integer, long or double (real) values.
-
-
-

Caution!

-

Be very careful: -This method doesn't typecheck the fields according to the table definition; -it just look whether or not it knows how to handle such types.

-
-
-
-

3.8   putline - writes a line to the server socket [DA]

-

Syntax:

-
-putline(line)
-
-
-
Parameters:
-
--- - - - -
line:line to be written (string)
-
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - -
TypeError:invalid connection, bad parameter type, or too many parameters
-
-
Description:
-
This method allows to directly write a string to the server socket.
-
-
-
-

3.9   getline - gets a line from server socket [DA]

-

Syntax:

-
-getline()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
string:the line read
-
-
Exceptions raised:
-
--- - - - - - - - -
TypeError:invalid connection
TypeError:too many parameters
MemoryError:buffer overflow
-
-
Description:
-
This method allows to directly read a string from the server socket.
-
-
-
-

3.10   endcopy - synchronizes client and server [DA]

-

Syntax:

-
-endcopy()
-
-
-
Parameters:
-
None
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - - - -
TypeError:invalid connection
TypeError:too many parameters
-
-
Description:
-
The use of direct access methods may desynchonize client and server. -This method ensure that client and server will be synchronized.
-
-
-
-

3.11   locreate - create a large object in the database [LO]

-

Syntax:

-
-locreate(mode)
-
-
-
Parameters:
-
--- - - - -
mode:large object create mode
-
-
Return type:
-
--- - - - -
pglarge:object handling the PostGreSQL large object
-
-
-

Exceptions raised:

-
- --- - - - - - - -
TypeError:invalid connection, bad parameter type, or too many parameters
pg.OperationalError:
 creation error
-
-
-
Description:
-
This method creates a large object in the database. The mode can be defined -by OR-ing the constants defined in the pg module (INV_READ, INV_WRITE and -INV_ARCHIVE). Please refer to PostgreSQL user manual for a description of -the mode values.
-
-
-
-

3.12   getlo - build a large object from given oid [LO]

-

Syntax:

-
-getlo(oid)
-
-
-
Parameters:
-
--- - - - -
oid:OID of the existing large object (integer)
-
-
Return type:
-
--- - - - -
pglarge:object handling the PostGreSQL large object
-
-
Exceptions raised:
-
--- - - - - - -
TypeError:invalid connection, bad parameter type, or too many parameters
ValueError:bad OID value (0 is invalid_oid)
-
-
Description:
-
This method allows to reuse a formerly created large object through the -pglarge interface, providing the user have its OID.
-
-
-
-

3.13   loimport - import a file to a large object [LO]

-

Syntax:

-
-loimport(name)
-
-
-
Parameters:
-
--- - - - -
name:the name of the file to be imported (string)
-
-
Return type:
-
--- - - - -
pglarge:object handling the PostGreSQL large object
-
-
Exceptions raised:
-
--- - - - - - - -
TypeError:invalid connection, bad argument type, or too many arguments
pg.OperationalError:
 error during file import
-
-
Description:
-
This methods allows to create large objects in a very simple way. You just -give the name of a file containing the data to be use.
-
-
-
-

3.14   Object attributes

-

Every pgobject defines a set of read-only attributes that describe the -connection and its status. These attributes are:

-
- --- - - - - - - - - - - - - - - - - - - - - - - -
host:the host name of the server (string)
port:the port of the server (integer)
db:the selected database (string)
options:the connection options (string)
tty:the connection debug terminal (string)
user:user name on the database system (string)
protocol_version:
 the frontend/backend protocol being used (integer)
server_version:the backend version (integer, e.g. 80305 for 8.3.5)
status:the status of the connection (integer: 1 - OK, 0 - bad)
error:the last warning/error message from the server (string)
-
-
-
-
-

4   The DB wrapper class

-

The pgobject methods are wrapped in the class DB. -The preferred way to use this module is as follows:

-
-import pg
-
-db = pg.DB(...) # see below
-
-for r in db.query( # just for example
-    """SELECT foo,bar
-       FROM foo_bar_table
-       WHERE foo !~ bar"""
-    ).dictresult():
-
-    print '%(foo)s %(bar)s' % r
-
-

This class can be subclassed as in this example:

-
-import pg
-
-class DB_ride(pg.DB):
-  """This class encapsulates the database functions and the specific
-     methods for the ride database."""
-
-  def __init__(self):
-      """Opens a database connection to the rides database"""
-
-      pg.DB.__init__(self, dbname = 'ride')
-      self.query("""SET DATESTYLE TO 'ISO'""")
-
-  [Add or override methods here]
-
-

The following describes the methods and variables of this class.

-
-

4.1   Initialization

-

The DB class is initialized with the same arguments as the connect -function described in section 2. It also initializes a few -internal variables. The statement db = DB() will open the -local database with the name of the user just like connect() does.

-

You can also initialize the DB class with an existing _pg or pgdb -connection. Pass this connection as a single unnamed parameter, or as a -single parameter named db. This allows you to use all of the methods -of the DB class with a DB-API 2 compliant connection. Note that the -close() and reopen() methods are inoperative in this case.

-
-
-

4.2   pkey - return the primary key of a table

-

Syntax:

-
-pkey(table)
-
-
-
Parameters:
-
--- - - - -
table:name of table
-
-
Return type:
-
--- - - - -
string:Name of the field which is the primary key of the table
-
-
Description:
-
This method returns the primary key of a table. For composite primary -keys, the return value will be a frozenset. Note that this raises an -exception if the table does not have a primary key.
-
-
-
-

4.3   get_databases - get list of databases in the system

-

Syntax:

-
-get_databases()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
list:all databases in the system
-
-
Description:
-
Although you can do this with a simple select, it is added here for -convenience.
-
-
-
-

4.4   get_relations - get list of relations in connected database

-

Syntax:

-
-get_relations(kinds)
-
-
-
Parameters:
-
--- - - - -
kinds:a string or sequence of type letters
-
-
Description:
-
The type letters are r = ordinary table, i = index, S = sequence, -v = view, c = composite type, s = special, t = TOAST table. -If kinds is None or an empty string, all relations are returned (this is -also the default). Although you can do this with a simple select, it is -added here for convenience.
-
-
-
-

4.5   get_tables - get list of tables in connected database

-

Syntax:

-
-get_tables()
-
-
-
Parameters:
-
None
-
Returns:
-
--- - - - -
list:all tables in connected database
-
-
Description:
-
Although you can do this with a simple select, it is added here for -convenience.
-
-
-
-

4.6   get_attnames - get the attribute names of a table

-

Syntax:

-
-get_attnames(table)
-
-
-
Parameters:
-
--- - - - -
table:name of table
-
-
Returns:
-
--- - - - -
dictionary:The keys are the attribute names, -the values are the type names of the attributes.
-
-
Description:
-
Given the name of a table, digs out the set of attribute names.
-
-
-
-

4.7   has_table_privilege - check whether current user has specified table privilege

-

Syntax:

-
-has_table_privilege(table, privilege)
-
-
-
Parameters:
-
--- - - - - - -
table:name of table
privilege:privilege to be checked - default is 'select'
-
-
Description:
-
Returns True if the current user has the specified privilege for the table.
-
-
-
-

4.8   get - get a row from a database table or view

-

Syntax:

-
-get(table, arg, [keyname])
-
-
-
Parameters:
-
--- - - - - - - - -
table:name of table or view
arg:either a dictionary or the value to be looked up
keyname:name of field to use as key (optional)
-
-
Return type:
-
--- - - - -
dictionary:The keys are the attribute names, -the values are the row values.
-
-
Description:
-
This method is the basic mechanism to get a single row. It assumes -that the key specifies a unique row. If keyname is not specified -then the primary key for the table is used. If arg is a dictionary -then the value for the key is taken from it and it is modified to -include the new values, replacing existing values where necessary. -For a composite key, keyname can also be a sequence of key names. -The OID is also put into the dictionary if the table has one, but in -order to allow the caller to work with multiple tables, it is munged -as oid(schema.table).
-
-
-
-

4.9   insert - insert a row into a database table

-

Syntax:

-
-insert(table, [d,] [return_changes,] [key = val, ...])
-
-
-
Parameters:
-
--- - - - - - - - -
table:name of table
d:optional dictionary of values
return_changes:Return values in new row - default True
-
-
Return type:
-
--- - - - -
dictionary:The dictionary of values inserted
-
-
Description:
-

This method inserts a row into a table. If the optional dictionary is -not supplied then the required values must be included as keyword/value -pairs. If a dictionary is supplied then any keywords provided will be -added to or replace the entry in the dictionary.

-

The dictionary is then, if possible, reloaded with the values actually -inserted in order to pick up values modified by rules, triggers, etc.

-

Due to the way that this function works in PostgreSQL versions below -8.2, you may find inserts taking longer and longer as your table gets -bigger. If this happens and it is a table with OID but no primary key -you can overcome this problem by simply adding an index onto the OID of -any table that you think may get large over time. You may also consider -using the inserttable() method described in section 3.

-

Note: With PostgreSQL versions before 8.2 the table being inserted to -must have a primary key or an OID to use this method properly. If not -then the dictionary will not be filled in as described. Also, if this -method is called within a transaction, the transaction will abort.

-

Note: The method currently doesn't support insert into views -although PostgreSQL does.

-
-
-
-
-

4.10   update - update a row in a database table

-

Syntax:

-
-update(table, [d,] [key = val, ...])
-
-
-
Parameters:
-
--- - - - - - -
table:name of table
d:optional dictionary of values
-
-
Return type:
-
--- - - - -
dictionary:the new row
-
-
Description:
-

Similar to insert but updates an existing row. The update is based on the -OID value as munged by get or passed as keyword, or on the primary key of -the table. The dictionary is modified, if possible, to reflect any changes -caused by the update due to triggers, rules, default values, etc.

-

Like insert, the dictionary is optional and updates will be performed -on the fields in the keywords. There must be an OID or primary key -either in the dictionary where the OID must be munged, or in the keywords -where it can be simply the string "oid".

-
-
-
-
-

4.11   clear - clears row values in memory

-

Syntax:

-
-clear(table, [a])
-
-
-
Parameters:
-
--- - - - - - -
table:name of table
a:optional dictionary of values
-
-
Return type:
-
--- - - - -
dictionary:an empty row
-
-
Description:
-

This method clears all the attributes to values determined by the types. -Numeric types are set to 0, Booleans are set to 'f', dates are set -to 'now()' and everything else is set to the empty string. -If the array argument is present, it is used as the array and any entries -matching attribute names are cleared with everything else left unchanged.

-

If the dictionary is not supplied a new one is created.

-
-
-
-
-

4.12   delete - delete a row from a database table

-

Syntax:

-
-delete(table, [d,] [key = val, ...])
-
-
-
Parameters:
-
--- - - - - - -
table:name of table
d:optional dictionary of values
-
-
Returns:
-
None
-
Description:
-
This method deletes the row from a table. It deletes based on the OID value -as munged by get or passed as keyword, or on the primary key of the table. -The return value is the number of deleted rows (i.e. 0 if the row did not -exist and 1 if the row was deleted).
-
-
-
-

4.13   escape_string - escape a string for use within SQL

-

Syntax:

-
-escape_string(string)
-
-
-
Parameters:
-
--- - - - -
string:the string that is to be escaped
-
-
Return type:
-
--- - - - -
str:the escaped string
-
-
Description:
-
Similar to the module function with the same name, but the -behavior of this method is adjusted depending on the connection properties -(such as character encoding).
-
-
-
-

4.14   escape_bytea - escape binary data for use within SQL as type bytea

-

Syntax:

-
-escape_bytea(datastring)
-
-
-
Parameters:
-
--- - - - -
datastring:string containing the binary data that is to be escaped
-
-
Return type:
-
--- - - - -
str:the escaped string
-
-
Description:
-
Similar to the module function with the same name, but the -behavior of this method is adjusted depending on the connection properties -(in particular, whether standard-conforming strings are enabled).
-
-
-
-

4.15   unescape_bytea -- unescape bytea data that has been retrieved as text

-

Syntax:

-
-unescape_bytea(string)
-
-
-
Parameters:
-
--- - - - -
datastring:the bytea data string that has been retrieved as text
-
-
Return type:
-
--- - - - -
str:string containing the binary data
-
-
Description:
-
See the module function with the same name.
-
-
-
-
-

5   pgqueryobject methods

-
-

5.1   getresult - get query values as list of tuples

-

Syntax:

-
-getresult()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
list:result values as a list of tuples
-
-
Exceptions raised:
-
--- - - - - - - -
TypeError:too many parameters
pg.InternalError:
 invalid previous result
-
-
Description:
-
This method returns the list of the values returned by the query. -More information about this result may be accessed using listfields(), -fieldname() and fieldnum() methods.
-
-
-
-

5.2   dictresult - get query values as list of dictionaries

-

Syntax:

-
-dictresult()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
list:result values as a list of dictionaries
-
-
Exceptions raised:
-
--- - - - - - - -
TypeError:too many parameters
pg.InternalError:
 invalid previous result
-
-
Description:
-
This method returns the list of the values returned by the query -with each tuple returned as a dictionary with the field names -used as the dictionary index.
-
-
-
-

5.3   listfields - lists fields names of previous query result

-

Syntax:

-
-listfields()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
list:field names
-
-
Exceptions raised:
-
--- - - - - - - -
TypeError:too many parameters
pg.InternalError:
 invalid previous result, or lost connection
-
-
Description:
-
This method returns the list of names of the fields defined for the -query result. The fields are in the same order as the result values.
-
-
-
-

5.4   fieldname, fieldnum - field name/number conversion

-

Syntax:

-
-fieldname(i)
-
-
-
Parameters:
-
--- - - - -
i:field number (integer)
-
-
Return type:
-
--- - - - -
string:field name
-
-
Exceptions raised:
-
--- - - - - - - - - -
TypeError:invalid connection, bad parameter type, or too many parameters
ValueError:invalid field number
pg.InternalError:
 invalid previous result, or lost connection
-
-
Description:
-
This method allows to find a field name from its rank number. It can be -useful for displaying a result. The fields are in the same order as the -result values.
-
-

Syntax:

-
-fieldnum(name)
-
-
-
Parameters:
-
--- - - - -
name:field name (string)
-
-
Return type:
-
--- - - - -
integer:field number
-
-
Exceptions raised:
-
--- - - - - - - - - -
TypeError:invalid connection, bad parameter type, or too many parameters
ValueError:unknown field name
pg.InternalError:
 invalid previous result, or lost connection
-
-
Description:
-
This method returns a field number from its name. It can be used to -build a function that converts result list strings to their correct -type, using a hardcoded table definition. The number returned is the -field rank in the result values list.
-
-
-
-

5.5   ntuples - return number of tuples in query object

-

Syntax:

-
-ntuples()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
integer:number of tuples in pgqueryobject
-
-
Exceptions raised:
-
--- - - - -
TypeError:Too many arguments.
-
-
Description:
-
This method returns the number of tuples found in a query.
-
-
-
-
-

6   Large objects: pglarge

-

This object handles all the request concerning a PostgreSQL large object. It -embeds and hides all the "recurrent" variables (object OID and connection), -exactly in the same way pgobjects do, thus only keeping significant -parameters in function calls. It keeps a reference to the pgobject used for -its creation, sending requests though with its parameters. Any modification but -dereferencing the pgobject will thus affect the pglarge object. -Dereferencing the initial pgobject is not a problem since Python won't -deallocate it before the pglarge object dereference it. -All functions return a generic error message on call error, whatever the -exact error was. The error attribute of the object allow to get the exact -error message.

-

See also the PostgreSQL programmer's guide for more information about the -large object interface.

-
-

6.1   open - opens a large object

-

Syntax:

-
-open(mode)
-
-
-
Parameters:
-
--- - - - -
mode:open mode definition (integer)
-
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - - - -
TypeError:invalid connection, bad parameter type, or too many parameters
IOError:already opened object, or open error
-
-
Description:
-
This method opens a large object for reading/writing, in the same way than -the Unix open() function. The mode value can be obtained by OR-ing the -constants defined in the pgmodule (INV_READ, INV_WRITE).
-
-
-
-

6.2   close - closes a large object

-

Syntax:

-
-close()
-
-
-
Parameters:
-
None
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - - - - - -
TypeError:invalid connection
TypeError:too many parameters
IOError:object is not opened, or close error
-
-
Description:
-
This method closes a previously opened large object, in the same way than -the Unix close() function.
-
-
-
-

6.3   read, write, tell, seek, unlink - file like large object handling

-

Syntax:

-
-read(size)
-
-
-
Parameters:
-
--- - - - -
size:maximal size of the buffer to be read
-
-
Return type:
-
--- - - - -
sized string:the read buffer
-
-
Exceptions raised:
-
--- - - - - - - - -
TypeError:invalid connection, invalid object, -bad parameter type, or too many parameters
ValueError:if size is negative
IOError:object is not opened, or read error
-
-
Description:
-
This function allows to read data from a large object, starting at current -position.
-
-

Syntax:

-
-write(string)
-
-
-
Parameters:
-
(sized) string - buffer to be written
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - - - -
TypeError:invalid connection, bad parameter type, or too many parameters
IOError:object is not opened, or write error
-
-
Description:
-
This function allows to write data to a large object, starting at current -position.
-
-

Syntax:

-
-seek(offset, whence)
-
-
-
Parameters:
-
--- - - - - - -
offset:position offset
whence:positional parameter
-
-
Return type:
-
--- - - - -
integer:new position in object
-
-
Exceptions raised:
-
--- - - - - - -
TypeError:binvalid connection or invalid object, -bad parameter type, or too many parameters
IOError:object is not opened, or seek error
-
-
Description:
-
This method allows to move the position cursor in the large object. The -whence parameter can be obtained by OR-ing the constants defined in the -pg module (SEEK_SET, SEEK_CUR, SEEK_END).
-
-

Syntax:

-
-tell()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
integer:current position in large object
-
-
Exceptions raised:
-
--- - - - - - - - -
TypeError:invalid connection or invalid object
TypeError:too many parameters
IOError:object is not opened, or seek error
-
-
Description:
-
This method allows to get the current position in the large object.
-
-

Syntax:

-
-unlink()
-
-
-
Parameter:
-
None
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - - - - - -
TypeError:invalid connection or invalid object
TypeError:too many parameters
IOError:object is not closed, or unlink error
-
-
Description:
-
This methods unlinks (deletes) the PostgreSQL large object.
-
-
-
-

6.4   size - gives the large object size

-

Syntax:

-
-size()
-
-
-
Parameters:
-
None
-
Return type:
-
--- - - - -
integer:the large object size
-
-
Exceptions raised:
-
--- - - - - - - - -
TypeError:invalid connection or invalid object
TypeError:too many parameters
IOError:object is not opened, or seek/tell error
-
-
Description:
-
This (composite) method allows to get the size of a large object. It was -implemented because this function is very useful for a web interfaced -database. Currently, the large object needs to be opened first.
-
-
-
-

6.5   export - saves a large object to a file

-

Syntax:

-
-export(name)
-
-
-
Parameters:
-
--- - - - -
name:file to be created
-
-
Return type:
-
None
-
Exceptions raised:
-
--- - - - - - -
TypeError:invalid connection or invalid object, -bad parameter type, or too many parameters
IOError:object is not closed, or export error
-
-
Description:
-
This methods allows to dump the content of a large object in a very simple -way. The exported file is created on the host of the program, not the -server host.
-
-
-
-

6.6   Object attributes

-

pglarge objects define a read-only set of attributes that allow to get -some information about it. These attributes are:

-
- --- - - - - - - - -
oid:the OID associated with the object
pgcnx:the pgobject associated with the object
error:the last warning/error message of the connection
-
-
-

Caution!

-

Be careful: -In multithreaded environments, error may be modified by another thread -using the same pgobject. Remember these object are shared, not duplicated. -You should provide some locking to be able if you want to check this. -The oid attribute is very interesting because it allow you reuse the OID -later, creating the pglarge object with a pgobject getlo() method call.

-
-
-
-
- - diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pg.txt b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pg.txt deleted file mode 100644 index ce98629abcba..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pg.txt +++ /dev/null @@ -1,1382 +0,0 @@ -================================ -PyGreSQL Programming Information -================================ - ------------------------------------------- -The classic PyGreSQL interface (pg module) ------------------------------------------- - -.. meta:: - :description: The classic PyGreSQL interface (pg module) - :keywords: PyGreSQL, pg, PostGreSQL, Python - -.. sectnum:: -.. contents:: Contents - - -Introduction -============ -You may either choose to use the -`"classic" PyGreSQL interface `_ -provided by the `pg` module or else the -`DB-API 2.0 compliant interface `_ -provided by the `pgdb` module. - -The following documentation covers only the older `pg` API. - -The `pg` module handles three types of objects, - -- the `pgobject`, which handles the connection - and all the requests to the database, -- the `pglarge` object, which handles - all the accesses to PostgreSQL large objects, -- the `pgqueryobject` that handles query results - -and it provides a convenient wrapper class `DB` for the `pgobject`. - -If you want to see a simple example of the use of some of these functions, -see http://ontario.bikerides.ca where you can find a link at the bottom to the -actual Python code for the page. - - -Module functions and constants -============================== -The `pg` module defines a few functions that allow to connect -to a database and to define "default variables" that override -the environment variables used by PostgreSQL. - -These "default variables" were designed to allow you to handle general -connection parameters without heavy code in your programs. You can prompt the -user for a value, put it in the default variable, and forget it, without -having to modify your environment. The support for default variables can be -disabled by setting the -DNO_DEF_VAR option in the Python setup file. Methods -relative to this are specified by the tag [DV]. - -All variables are set to `None` at module initialization, specifying that -standard environment variables should be used. - -connect - opens a pg connection -------------------------------- -Syntax:: - - connect([dbname], [host], [port], [opt], [tty], [user], [passwd]) - -Parameters: - :dbname: name of connected database (string/None) - :host: name of the server host (string/None) - :port: port used by the database server (integer/-1) - :opt: connection options (string/None) - :tty: debug terminal (string/None) - :user: PostgreSQL user (string/None) - :passwd: password for user (string/None) - -Return type: - :pgobject: If successful, the `pgobject` handling the connection - -Exceptions raised: - :TypeError: bad argument type, or too many arguments - :SyntaxError: duplicate argument definition - :pg.InternalError: some error occurred during pg connection definition - - (plus all exceptions relative to object allocation) - -Description: - This function opens a connection to a specified database on a given - PostgreSQL server. You can use keywords here, as described in the - Python tutorial. The names of the keywords are the name of the - parameters given in the syntax line. For a precise description - of the parameters, please refer to the PostgreSQL user manual. - -Examples:: - - import pg - - con1 = pg.connect('testdb', 'myhost', 5432, None, None, 'bob', None) - con2 = pg.connect(dbname='testdb', host='localhost', user='bob') - -get_defhost, set_defhost - default server host [DV] ---------------------------------------------------- -Syntax:: - - get_defhost() - -Parameters: - None - -Return type: - :string, None: default host specification - -Exceptions raised: - :TypeError: too many arguments - -Description: - This method returns the current default host specification, - or `None` if the environment variables should be used. - Environment variables won't be looked up. - -Syntax:: - - set_defhost(host) - -Parameters: - :host: new default host (string/None) - -Return type: - :string, None: previous default host specification - -Exceptions raised: - :TypeError: bad argument type, or too many arguments - -Description: - This methods sets the default host value for new connections. - If `None` is supplied as parameter, environment variables will - be used in future connections. It returns the previous setting - for default host. - -get_defport, set_defport - default server port [DV] ---------------------------------------------------- -Syntax:: - - get_defport() - -Parameters: - None - -Return type: - :integer, None: default port specification - -Exceptions raised: - :TypeError: too many arguments - -Description: - This method returns the current default port specification, - or `None` if the environment variables should be used. - Environment variables won't be looked up. - -Syntax:: - - set_defport(port) - -Parameters: - :port: new default port (integer/-1) - -Return type: - :integer, None: previous default port specification - -Description: - This methods sets the default port value for new connections. If -1 is - supplied as parameter, environment variables will be used in future - connections. It returns the previous setting for default port. - -get_defopt, set_defopt - default connection options [DV] --------------------------------------------------------- -Syntax:: - - get_defopt() - -Parameters: - None - -Return type: - :string, None: default options specification - -Exceptions raised: - :TypeError: too many arguments - -Description: - This method returns the current default connection options specification, - or `None` if the environment variables should be used. Environment variables - won't be looked up. - -Syntax:: - - set_defopt(options) - -Parameters: - :options: new default connection options (string/None) - -Return type: - :string, None: previous default options specification - -Exceptions raised: - :TypeError: bad argument type, or too many arguments - -Description: - This methods sets the default connection options value for new connections. - If `None` is supplied as parameter, environment variables will be used in - future connections. It returns the previous setting for default options. - -get_deftty, set_deftty - default debug tty [DV] ------------------------------------------------ -Syntax:: - - get_deftty() - -Parameters: - None - -Return type: - :string, None: default debug terminal specification - -Exceptions raised: - :TypeError: too many arguments - -Description: - This method returns the current default debug terminal specification, or - `None` if the environment variables should be used. Environment variables - won't be looked up. - -Syntax:: - - set_deftty(terminal) - -Parameters: - :terminal: new default debug terminal (string/None) - -Return type: - :string, None: previous default debug terminal specification - -Exceptions raised: - :TypeError: bad argument type, or too many arguments - -Description: - This methods sets the default debug terminal value for new connections. If - `None` is supplied as parameter, environment variables will be used in future - connections. It returns the previous setting for default terminal. - -get_defbase, set_defbase - default database name [DV] ------------------------------------------------------ -Syntax:: - - get_defbase() - -Parameters: - None - -Return type: - :string, None: default database name specification - -Exceptions raised: - :TypeError: too many arguments - -Description: - This method returns the current default database name specification, or - `None` if the environment variables should be used. Environment variables - won't be looked up. - -Syntax:: - - set_defbase(base) - -Parameters: - :base: new default base name (string/None) - -Return type: - :string, None: previous default database name specification - -Exceptions raised: - :TypeError: bad argument type, or too many arguments - -Description: - This method sets the default database name value for new connections. If - `None` is supplied as parameter, environment variables will be used in - future connections. It returns the previous setting for default host. - -escape_string - escape a string for use within SQL --------------------------------------------------- -Syntax:: - - escape_string(string) - -Parameters: - :string: the string that is to be escaped - -Return type: - :str: the escaped string - -Exceptions raised: - :TypeError: bad argument type, or too many arguments - -Description: - This function escapes a string for use within an SQL command. - This is useful when inserting data values as literal constants - in SQL commands. Certain characters (such as quotes and backslashes) - must be escaped to prevent them from being interpreted specially - by the SQL parser. `escape_string` performs this operation. - Note that there is also a `pgobject` method with the same name - which takes connection properties into account. - -.. caution:: It is especially important to do proper escaping when - handling strings that were received from an untrustworthy source. - Otherwise there is a security risk: you are vulnerable to "SQL injection" - attacks wherein unwanted SQL commands are fed to your database. - -Example:: - - name = raw_input("Name? ") - phone = con.query("select phone from employees" - " where name='%s'" % escape_string(name)).getresult() - -escape_bytea - escape binary data for use within SQL as type `bytea` --------------------------------------------------------------------- -Syntax:: - - escape_bytea(datastring) - -Parameters: - :datastring: string containing the binary data that is to be escaped - -Return type: - :str: the escaped string - -Exceptions raised: - :TypeError: bad argument type, or too many arguments - -Description: - Escapes binary data for use within an SQL command with the type `bytea`. - As with `escape_string`, this is only used when inserting data directly - into an SQL command string. - Note that there is also a `pgobject` method with the same name - which takes connection properties into account. - -Example:: - - picture = file('garfield.gif', 'rb').read() - con.query("update pictures set img='%s' where name='Garfield'" - % escape_bytea(picture)) - -unescape_bytea -- unescape `bytea` data that has been retrieved as text ------------------------------------------------------------------------ -Syntax:: - - unescape_bytea(string) - -Parameters: - :datastring: the `bytea` data string that has been retrieved as text - -Return type: - :str: string containing the binary data - -Exceptions raised: - :TypeError: bad argument type, or too many arguments - -Description: - Converts an escaped string representation of binary data into binary - data - the reverse of `escape_bytea`. This is needed when retrieving - `bytea` data with the `getresult()` or `dictresult()` method. - -Example:: - - picture = unescape_bytea(con.query( - "select img from pictures where name='Garfield'").getresult[0][0]) - file('garfield.gif', 'wb').write(picture) - -set_decimal -- set a decimal type to be used for numeric values ---------------------------------------------------------------- -Syntax:: - - set_decimal(cls) - -Parameters: - :cls: the Python class to be used for PostgreSQL numeric values - -Description: - This function can be used to specify the Python class that shall be - used by PyGreSQL to hold PostgreSQL numeric values. The default class - is decimal.Decimal if available, otherwise the float type is used. - -Module constants ----------------- -Some constants are defined in the module dictionary. -They are intended to be used as parameters for methods calls. -You should refer to the libpq description in the PostgreSQL user manual -for more information about them. These constants are: - -:version, __version__: constants that give the current version. -:INV_READ, INV_WRITE: large objects access modes, - used by `(pgobject.)locreate` and `(pglarge.)open` -:SEEK_SET, SEEK_CUR, SEEK_END: positional flags, - used by `(pglarge.)seek` - - -Connection objects: pgobject -============================ -This object handles a connection to a PostgreSQL database. It embeds and -hides all the parameters that define this connection, thus just leaving really -significant parameters in function calls. - -.. caution:: Some methods give direct access to the connection socket. - *Do not use them unless you really know what you are doing.* - If you prefer disabling them, - set the -DNO_DIRECT option in the Python setup file. - - **These methods are specified by the tag [DA].** - -.. note:: Some other methods give access to large objects - (refer to PostgreSQL user manual for more information about these). - If you want to forbid access to these from the module, - set the -DNO_LARGE option in the Python setup file. - - **These methods are specified by the tag [LO].** - -query - executes a SQL command string -------------------------------------- -Syntax:: - - query(command) - -Parameters: - :command: SQL command (string) - -Return type: - :pgqueryobject, None: result values - -Exceptions raised: - :TypeError: bad argument type, or too many arguments - :ValueError: empty SQL query or lost connection - :pg.ProgrammingError: error in query - :pg.InternalError': error during query processing - -Description: - This method simply sends a SQL query to the database. If the query is an - insert statement that inserted exactly one row into a table that has OIDs, the - return value is the OID of the newly inserted row. If the query is an update - or delete statement, or an insert statement that did not insert exactly one - row in a table with OIDs, then the numer of rows affected is returned as a - string. If it is a statement that returns rows as a result (usually a select - statement, but maybe also an "insert/update ... returning" statement), this - method returns a `pgqueryobject` that can be accessed via the `getresult()` - or `dictresult()` method or simply printed. Otherwise, it returns `None`. - -reset - resets the connection ------------------------------ -Syntax:: - - reset() - -Parameters: - None - -Return type: - None - -Exceptions raised: - :TypeError: too many (any) arguments - -Description: - This method resets the current database connection. - -cancel - abandon processing of current SQL command --------------------------------------------------- -Syntax:: - - cancel() - -Parameters: - None - -Return type: - None - -Exceptions raised: - :TypeError: too many (any) arguments - -Description: - This method requests that the server abandon processing - of the current SQL command. - -close - close the database connection -------------------------------------- -Syntax:: - - close() - -Parameters: - None - -Return type: - None - -Exceptions raised: - :TypeError: too many (any) arguments - -Description: - This method closes the database connection. The connection will - be closed in any case when the connection is deleted but this - allows you to explicitly close it. It is mainly here to allow - the DB-SIG API wrapper to implement a close function. - -fileno - returns the socket used to connect to the database ------------------------------------------------------------ -Syntax:: - - fileno() - -Parameters: - None - -Exceptions raised: - :TypeError: too many (any) arguments - -Description: - This method returns the underlying socket id used to connect - to the database. This is useful for use in select calls, etc. - -getnotify - gets the last notify from the server ------------------------------------------------- -Syntax:: - - getnotify() - -Parameters: - None - -Return type: - :tuple, None: last notify from server - -Exceptions raised: - :TypeError: too many parameters - :TypeError: invalid connection - -Description: - This methods try to get a notify from the server (from the SQL statement - NOTIFY). If the server returns no notify, the methods returns None. - Otherwise, it returns a tuple (couple) `(relname, pid)`, where `relname` - is the name of the notify and `pid` the process id of the connection that - triggered the notify. Remember to do a listen query first otherwise - getnotify() will always return `None`. - -inserttable - insert a list into a table ----------------------------------------- -Syntax:: - - inserttable(table, values) - -Parameters: - :table: the table name (string) - :values: list of rows values (list) - -Return type: - None - -Exceptions raised: - :TypeError: invalid connection, bad argument type, or too many arguments - :MemoryError: insert buffer could not be allocated - :ValueError: unsupported values - -Description: - This method allow to *quickly* insert large blocks of data in a table: - It inserts the whole values list into the given table. Internally, it - uses the COPY command of the PostgreSQL database. The list is a list - of tuples/lists that define the values for each inserted row. The rows - values may contain string, integer, long or double (real) values. - -.. caution:: *Be very careful*: - This method doesn't typecheck the fields according to the table definition; - it just look whether or not it knows how to handle such types. - -putline - writes a line to the server socket [DA] -------------------------------------------------- -Syntax:: - - putline(line) - -Parameters: - :line: line to be written (string) - -Return type: - None - -Exceptions raised: - :TypeError: invalid connection, bad parameter type, or too many parameters - -Description: - This method allows to directly write a string to the server socket. - -getline - gets a line from server socket [DA] ---------------------------------------------- -Syntax:: - - getline() - -Parameters: - None - -Return type: - :string: the line read - -Exceptions raised: - :TypeError: invalid connection - :TypeError: too many parameters - :MemoryError: buffer overflow - -Description: - This method allows to directly read a string from the server socket. - -endcopy - synchronizes client and server [DA] ---------------------------------------------- -Syntax:: - - endcopy() - -Parameters: - None - -Return type: - None - -Exceptions raised: - :TypeError: invalid connection - :TypeError: too many parameters - -Description: - The use of direct access methods may desynchonize client and server. - This method ensure that client and server will be synchronized. - -locreate - create a large object in the database [LO] ------------------------------------------------------ -Syntax:: - - locreate(mode) - -Parameters: - :mode: large object create mode - -Return type: - :pglarge: object handling the PostGreSQL large object - -Exceptions raised: - - :TypeError: invalid connection, bad parameter type, or too many parameters - :pg.OperationalError: creation error - -Description: - This method creates a large object in the database. The mode can be defined - by OR-ing the constants defined in the pg module (INV_READ, INV_WRITE and - INV_ARCHIVE). Please refer to PostgreSQL user manual for a description of - the mode values. - -getlo - build a large object from given oid [LO] ------------------------------------------------- -Syntax:: - - getlo(oid) - -Parameters: - :oid: OID of the existing large object (integer) - -Return type: - :pglarge: object handling the PostGreSQL large object - -Exceptions raised: - :TypeError: invalid connection, bad parameter type, or too many parameters - :ValueError: bad OID value (0 is invalid_oid) - -Description: - This method allows to reuse a formerly created large object through the - `pglarge` interface, providing the user have its OID. - -loimport - import a file to a large object [LO] ------------------------------------------------ -Syntax:: - - loimport(name) - -Parameters: - :name: the name of the file to be imported (string) - -Return type: - :pglarge: object handling the PostGreSQL large object - -Exceptions raised: - :TypeError: invalid connection, bad argument type, or too many arguments - :pg.OperationalError: error during file import - -Description: - This methods allows to create large objects in a very simple way. You just - give the name of a file containing the data to be use. - -Object attributes ------------------ -Every `pgobject` defines a set of read-only attributes that describe the -connection and its status. These attributes are: - - :host: the host name of the server (string) - :port: the port of the server (integer) - :db: the selected database (string) - :options: the connection options (string) - :tty: the connection debug terminal (string) - :user: user name on the database system (string) - :protocol_version: the frontend/backend protocol being used (integer) - :server_version: the backend version (integer, e.g. 80305 for 8.3.5) - :status: the status of the connection (integer: 1 - OK, 0 - bad) - :error: the last warning/error message from the server (string) - - -The DB wrapper class -==================== -The `pgobject` methods are wrapped in the class `DB`. -The preferred way to use this module is as follows:: - - import pg - - db = pg.DB(...) # see below - - for r in db.query( # just for example - """SELECT foo,bar - FROM foo_bar_table - WHERE foo !~ bar""" - ).dictresult(): - - print '%(foo)s %(bar)s' % r - -This class can be subclassed as in this example:: - - import pg - - class DB_ride(pg.DB): - """This class encapsulates the database functions and the specific - methods for the ride database.""" - - def __init__(self): - """Opens a database connection to the rides database""" - - pg.DB.__init__(self, dbname = 'ride') - self.query("""SET DATESTYLE TO 'ISO'""") - - [Add or override methods here] - -The following describes the methods and variables of this class. - -Initialization --------------- -The DB class is initialized with the same arguments as the connect -function described in section 2. It also initializes a few -internal variables. The statement `db = DB()` will open the -local database with the name of the user just like connect() does. - -You can also initialize the DB class with an existing `_pg` or `pgdb` -connection. Pass this connection as a single unnamed parameter, or as a -single parameter named `db`. This allows you to use all of the methods -of the DB class with a DB-API 2 compliant connection. Note that the -`close()` and `reopen()` methods are inoperative in this case. - - - -pkey - return the primary key of a table ----------------------------------------- -Syntax:: - - pkey(table) - -Parameters: - :table: name of table - -Return type: - :string: Name of the field which is the primary key of the table - -Description: - This method returns the primary key of a table. For composite primary - keys, the return value will be a frozenset. Note that this raises an - exception if the table does not have a primary key. - -get_databases - get list of databases in the system ---------------------------------------------------- -Syntax:: - - get_databases() - -Parameters: - None - -Return type: - :list: all databases in the system - -Description: - Although you can do this with a simple select, it is added here for - convenience. - -get_relations - get list of relations in connected database ------------------------------------------------------------ -Syntax:: - - get_relations(kinds) - -Parameters: - :kinds: a string or sequence of type letters - -Description: - The type letters are `r` = ordinary table, `i` = index, `S` = sequence, - `v` = view, `c` = composite type, `s` = special, `t` = TOAST table. - If `kinds` is None or an empty string, all relations are returned (this is - also the default). Although you can do this with a simple select, it is - added here for convenience. - -get_tables - get list of tables in connected database ------------------------------------------------------ -Syntax:: - - get_tables() - -Parameters: - None - -Returns: - :list: all tables in connected database - -Description: - Although you can do this with a simple select, it is added here for - convenience. - -get_attnames - get the attribute names of a table -------------------------------------------------- -Syntax:: - - get_attnames(table) - -Parameters: - :table: name of table - -Returns: - :dictionary: The keys are the attribute names, - the values are the type names of the attributes. - -Description: - Given the name of a table, digs out the set of attribute names. - -has_table_privilege - check whether current user has specified table privilege ------------------------------------------------------------------------------- -Syntax:: - - has_table_privilege(table, privilege) - -Parameters: - :table: name of table - :privilege: privilege to be checked - default is 'select' - -Description: - Returns True if the current user has the specified privilege for the table. - -get - get a row from a database table or view ---------------------------------------------- -Syntax:: - - get(table, arg, [keyname]) - -Parameters: - :table: name of table or view - :arg: either a dictionary or the value to be looked up - :keyname: name of field to use as key (optional) - -Return type: - :dictionary: The keys are the attribute names, - the values are the row values. - -Description: - This method is the basic mechanism to get a single row. It assumes - that the key specifies a unique row. If `keyname` is not specified - then the primary key for the table is used. If `arg` is a dictionary - then the value for the key is taken from it and it is modified to - include the new values, replacing existing values where necessary. - For a composite key, `keyname` can also be a sequence of key names. - The OID is also put into the dictionary if the table has one, but in - order to allow the caller to work with multiple tables, it is munged - as `oid(schema.table)`. - -insert - insert a row into a database table -------------------------------------------- -Syntax:: - - insert(table, [d,] [return_changes,] [key = val, ...]) - -Parameters: - :table: name of table - :d: optional dictionary of values - :return_changes: Return values in new row - default True - -Return type: - :dictionary: The dictionary of values inserted - -Description: - This method inserts a row into a table. If the optional dictionary is - not supplied then the required values must be included as keyword/value - pairs. If a dictionary is supplied then any keywords provided will be - added to or replace the entry in the dictionary. - - The dictionary is then, if possible, reloaded with the values actually - inserted in order to pick up values modified by rules, triggers, etc. - - Due to the way that this function works in PostgreSQL versions below - 8.2, you may find inserts taking longer and longer as your table gets - bigger. If this happens and it is a table with OID but no primary key - you can overcome this problem by simply adding an index onto the OID of - any table that you think may get large over time. You may also consider - using the inserttable() method described in section 3. - - Note: With PostgreSQL versions before 8.2 the table being inserted to - must have a primary key or an OID to use this method properly. If not - then the dictionary will not be filled in as described. Also, if this - method is called within a transaction, the transaction will abort. - - Note: The method currently doesn't support insert into views - although PostgreSQL does. - -update - update a row in a database table ------------------------------------------ -Syntax:: - - update(table, [d,] [key = val, ...]) - -Parameters: - :table: name of table - :d: optional dictionary of values - -Return type: - :dictionary: the new row - -Description: - Similar to insert but updates an existing row. The update is based on the - OID value as munged by get or passed as keyword, or on the primary key of - the table. The dictionary is modified, if possible, to reflect any changes - caused by the update due to triggers, rules, default values, etc. - - Like insert, the dictionary is optional and updates will be performed - on the fields in the keywords. There must be an OID or primary key - either in the dictionary where the OID must be munged, or in the keywords - where it can be simply the string "oid". - -clear - clears row values in memory ------------------------------------ -Syntax:: - - clear(table, [a]) - -Parameters: - :table: name of table - :a: optional dictionary of values - -Return type: - :dictionary: an empty row - -Description: - This method clears all the attributes to values determined by the types. - Numeric types are set to 0, Booleans are set to 'f', dates are set - to 'now()' and everything else is set to the empty string. - If the array argument is present, it is used as the array and any entries - matching attribute names are cleared with everything else left unchanged. - - If the dictionary is not supplied a new one is created. - -delete - delete a row from a database table -------------------------------------------- -Syntax:: - - delete(table, [d,] [key = val, ...]) - -Parameters: - :table: name of table - :d: optional dictionary of values - -Returns: - None - -Description: - This method deletes the row from a table. It deletes based on the OID value - as munged by get or passed as keyword, or on the primary key of the table. - The return value is the number of deleted rows (i.e. 0 if the row did not - exist and 1 if the row was deleted). - -escape_string - escape a string for use within SQL --------------------------------------------------- -Syntax:: - - escape_string(string) - -Parameters: - :string: the string that is to be escaped - -Return type: - :str: the escaped string - -Description: - Similar to the module function with the same name, but the - behavior of this method is adjusted depending on the connection properties - (such as character encoding). - -escape_bytea - escape binary data for use within SQL as type `bytea` --------------------------------------------------------------------- -Syntax:: - - escape_bytea(datastring) - -Parameters: - :datastring: string containing the binary data that is to be escaped - -Return type: - :str: the escaped string - -Description: - Similar to the module function with the same name, but the - behavior of this method is adjusted depending on the connection properties - (in particular, whether standard-conforming strings are enabled). - -unescape_bytea -- unescape `bytea` data that has been retrieved as text ------------------------------------------------------------------------ -Syntax:: - - unescape_bytea(string) - -Parameters: - :datastring: the `bytea` data string that has been retrieved as text - -Return type: - :str: string containing the binary data - -Description: - See the module function with the same name. - - -pgqueryobject methods -===================== - -getresult - get query values as list of tuples ------------------------------------------------ -Syntax:: - - getresult() - -Parameters: - None - -Return type: - :list: result values as a list of tuples - -Exceptions raised: - :TypeError: too many parameters - :pg.InternalError: invalid previous result - -Description: - This method returns the list of the values returned by the query. - More information about this result may be accessed using listfields(), - fieldname() and fieldnum() methods. - -dictresult - get query values as list of dictionaries ------------------------------------------------------ -Syntax:: - - dictresult() - -Parameters: - None - -Return type: - :list: result values as a list of dictionaries - -Exceptions raised: - :TypeError: too many parameters - :pg.InternalError: invalid previous result - -Description: - This method returns the list of the values returned by the query - with each tuple returned as a dictionary with the field names - used as the dictionary index. - - -listfields - lists fields names of previous query result --------------------------------------------------------- -Syntax:: - - listfields() - -Parameters: - None - -Return type: - :list: field names - -Exceptions raised: - :TypeError: too many parameters - :pg.InternalError: invalid previous result, or lost connection - -Description: - This method returns the list of names of the fields defined for the - query result. The fields are in the same order as the result values. - -fieldname, fieldnum - field name/number conversion --------------------------------------------------- -Syntax:: - - fieldname(i) - -Parameters: - :i: field number (integer) - -Return type: - :string: field name - -Exceptions raised: - :TypeError: invalid connection, bad parameter type, or too many parameters - :ValueError: invalid field number - :pg.InternalError: invalid previous result, or lost connection - -Description: - This method allows to find a field name from its rank number. It can be - useful for displaying a result. The fields are in the same order as the - result values. - -Syntax:: - - fieldnum(name) - -Parameters: - :name: field name (string) - -Return type: - :integer: field number - -Exceptions raised: - :TypeError: invalid connection, bad parameter type, or too many parameters - :ValueError: unknown field name - :pg.InternalError: invalid previous result, or lost connection - -Description: - This method returns a field number from its name. It can be used to - build a function that converts result list strings to their correct - type, using a hardcoded table definition. The number returned is the - field rank in the result values list. - -ntuples - return number of tuples in query object -------------------------------------------------- -Syntax:: - - ntuples() - -Parameters: - None - -Return type: - :integer: number of tuples in `pgqueryobject` - -Exceptions raised: - :TypeError: Too many arguments. - -Description: - This method returns the number of tuples found in a query. - - -Large objects: pglarge -====================== -This object handles all the request concerning a PostgreSQL large object. It -embeds and hides all the "recurrent" variables (object OID and connection), -exactly in the same way `pgobjects` do, thus only keeping significant -parameters in function calls. It keeps a reference to the `pgobject` used for -its creation, sending requests though with its parameters. Any modification but -dereferencing the `pgobject` will thus affect the `pglarge` object. -Dereferencing the initial `pgobject` is not a problem since Python won't -deallocate it before the `pglarge` object dereference it. -All functions return a generic error message on call error, whatever the -exact error was. The `error` attribute of the object allow to get the exact -error message. - -See also the PostgreSQL programmer's guide for more information about the -large object interface. - -open - opens a large object ---------------------------- -Syntax:: - - open(mode) - -Parameters: - :mode: open mode definition (integer) - -Return type: - None - -Exceptions raised: - :TypeError: invalid connection, bad parameter type, or too many parameters - :IOError: already opened object, or open error - -Description: - This method opens a large object for reading/writing, in the same way than - the Unix open() function. The mode value can be obtained by OR-ing the - constants defined in the pgmodule (INV_READ, INV_WRITE). - -close - closes a large object ------------------------------ -Syntax:: - - close() - -Parameters: - None - -Return type: - None - -Exceptions raised: - :TypeError: invalid connection - :TypeError: too many parameters - :IOError: object is not opened, or close error - -Description: - This method closes a previously opened large object, in the same way than - the Unix close() function. - -read, write, tell, seek, unlink - file like large object handling ------------------------------------------------------------------ -Syntax:: - - read(size) - -Parameters: - :size: maximal size of the buffer to be read - -Return type: - :sized string: the read buffer - -Exceptions raised: - :TypeError: invalid connection, invalid object, - bad parameter type, or too many parameters - :ValueError: if `size` is negative - :IOError: object is not opened, or read error - -Description: - This function allows to read data from a large object, starting at current - position. - -Syntax:: - - write(string) - -Parameters: - (sized) string - buffer to be written - -Return type: - None - -Exceptions raised: - :TypeError: invalid connection, bad parameter type, or too many parameters - :IOError: object is not opened, or write error - -Description: - This function allows to write data to a large object, starting at current - position. - -Syntax:: - - seek(offset, whence) - -Parameters: - :offset: position offset - :whence: positional parameter - -Return type: - :integer: new position in object - -Exceptions raised: - :TypeError: binvalid connection or invalid object, - bad parameter type, or too many parameters - :IOError: object is not opened, or seek error - -Description: - This method allows to move the position cursor in the large object. The - whence parameter can be obtained by OR-ing the constants defined in the - `pg` module (`SEEK_SET`, `SEEK_CUR`, `SEEK_END`). - -Syntax:: - - tell() - -Parameters: - None - -Return type: - :integer: current position in large object - -Exceptions raised: - :TypeError: invalid connection or invalid object - :TypeError: too many parameters - :IOError: object is not opened, or seek error - -Description: - This method allows to get the current position in the large object. - -Syntax:: - - unlink() - -Parameter: - None - -Return type: - None - -Exceptions raised: - :TypeError: invalid connection or invalid object - :TypeError: too many parameters - :IOError: object is not closed, or unlink error - -Description: - This methods unlinks (deletes) the PostgreSQL large object. - -size - gives the large object size ----------------------------------- - -Syntax:: - - size() - -Parameters: - None - -Return type: - :integer: the large object size - -Exceptions raised: - :TypeError: invalid connection or invalid object - :TypeError: too many parameters - :IOError: object is not opened, or seek/tell error - -Description: - This (composite) method allows to get the size of a large object. It was - implemented because this function is very useful for a web interfaced - database. Currently, the large object needs to be opened first. - -export - saves a large object to a file ---------------------------------------- -Syntax:: - - export(name) - -Parameters: - :name: file to be created - -Return type: - None - -Exceptions raised: - :TypeError: invalid connection or invalid object, - bad parameter type, or too many parameters - :IOError: object is not closed, or export error - -Description: - This methods allows to dump the content of a large object in a very simple - way. The exported file is created on the host of the program, not the - server host. - -Object attributes ------------------ -`pglarge` objects define a read-only set of attributes that allow to get -some information about it. These attributes are: - - :oid: the OID associated with the object - :pgcnx: the `pgobject` associated with the object - :error: the last warning/error message of the connection - -.. caution:: *Be careful*: - In multithreaded environments, `error` may be modified by another thread - using the same pgobject. Remember these object are shared, not duplicated. - You should provide some locking to be able if you want to check this. - The `oid` attribute is very interesting because it allow you reuse the OID - later, creating the `pglarge` object with a `pgobject` getlo() method call. diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pgdb.html b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pgdb.html deleted file mode 100644 index 84962f44684b..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pgdb.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - -PyGreSQL Programming Information - - - - - -
-

PyGreSQL Programming Information

-

The DB-API compliant interface (pgdb module)

- -
-

1   Introduction

-

You may either choose to use the -"classic" PyGreSQL interface -provided by the pg module or else the -DB-API 2.0 compliant interface -provided by the pgdb module.

-

DB-API 2.0 -(Python Database API Specification v2.0) -is a specification for connecting to databases (not only PostGreSQL) -from Python that has been developed by the Python DB-SIG in 1999.

-

The following documentation covers only the newer pgdb API.

-
-
The authoritative programming information for the DB-API is availabe at
-
http://www.python.org/dev/peps/pep-0249/
-
A tutorial-like introduction to the DB-API can be found at
-
http://www2.linuxjournal.com/lj-issues/issue49/2605.html
-
-
-
-

2   The pgdb module

-
-

Note

-

This section of the documentation still needs to be written.

-
-
-
- - diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pgdb.txt b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pgdb.txt deleted file mode 100644 index b333c01640d1..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/pgdb.txt +++ /dev/null @@ -1,42 +0,0 @@ -================================ -PyGreSQL Programming Information -================================ - --------------------------------------------- -The DB-API compliant interface (pgdb module) --------------------------------------------- - -.. meta:: - :description: The DB-API compliant interface (pgdb module) - :keywords: PyGreSQL, pgdb, DB-API, PostGreSQL, Python - -.. sectnum:: -.. contents:: Contents - - -Introduction -============ -You may either choose to use the -`"classic" PyGreSQL interface `_ -provided by the `pg` module or else the -`DB-API 2.0 compliant interface `_ -provided by the `pgdb` module. - -`DB-API 2.0 `_ -(Python Database API Specification v2.0) -is a specification for connecting to databases (not only PostGreSQL) -from Python that has been developed by the Python DB-SIG in 1999. - -The following documentation covers only the newer `pgdb` API. - -The authoritative programming information for the DB-API is availabe at - http://www.python.org/dev/peps/pep-0249/ - -A tutorial-like introduction to the DB-API can be found at - http://www2.linuxjournal.com/lj-issues/issue49/2605.html - - -The pgdb module -=============== -.. note:: This section of the documentation still needs to be written. - diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/readme.html b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/readme.html deleted file mode 100644 index 656f8cd7d4c1..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/readme.html +++ /dev/null @@ -1,243 +0,0 @@ - - - - - - -PyGreSQL - Python interface for PostgreSQL - - - - - -
-

PyGreSQL - Python interface for PostgreSQL

-

PyGreSQL version 4.0

- -
-

Copyright notice

-

Written by D'Arcy J.M. Cain (darcy@druid.net)

-

Based heavily on code written by Pascal Andre (andre@chimay.via.ecp.fr)

-

Copyright (c) 1995, Pascal Andre

-

Further modifications copyright (c) 1997-2008 by D'Arcy J.M. Cain -(darcy@druid.net)

-

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 or in any -new file that contains a substantial portion of this file.

-

IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE -AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-

THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE -AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, -ENHANCEMENTS, OR MODIFICATIONS.

-
-
-

Introduction

-

PostgreSQL is a highly scalable, SQL compliant, open source -object-relational database management system. With more than 15 years -of development history, it is quickly becoming the de facto database -for enterprise level open source solutions. -Best of all, PostgreSQL's source code is available under the most liberal -open source license: the BSD license.

-

Python Python is an interpreted, interactive, object-oriented -programming language. It is often compared to Tcl, Perl, Scheme or Java. -Python combines remarkable power with very clear syntax. It has modules, -classes, exceptions, very high level dynamic data types, and dynamic typing. -There are interfaces to many system calls and libraries, as well as to -various windowing systems (X11, Motif, Tk, Mac, MFC). New built-in modules -are easily written in C or C++. Python is also usable as an extension -language for applications that need a programmable interface. -The Python implementation is copyrighted but freely usable and distributable, -even for commercial use.

-

PyGreSQL is a Python module that interfaces to a PostgreSQL database. -It embeds the PostgreSQL query library to allow easy use of the powerful -PostgreSQL features from a Python script.

-

PyGreSQL is developed and tested on a NetBSD system, but it should also -run on most other platforms where PostgreSQL and Python is running. -It is based on the PyGres95 code written by Pascal Andre (andre@chimay.via.ecp.fr). -D'Arcy (darcy@druid.net) renamed it to PyGreSQL starting with -version 2.0 and serves as the "BDFL" of PyGreSQL.

-

The current version PyGreSQL 4.0 needs PostgreSQL 7.2 and Python 2.3 or above.

-
-
-

Where to get ... ?

- -
-

Download PyGreSQL here

-
-
The released version of the source code is available at
-
-
-
You can also check the latest pre-release version at
-
-
-
A Linux RPM can be picked up from
-
-
-
A NetBSD package is available in their pkgsrc collection
-
-
-
A FreeBSD package is available in their ports collection
-
-
-
A Win32 package for various Python versions is available at
-
-
-
You can also find PyGreSQL on the Python Package Index at
-
-
-
-
-
-
-

Distribution files

- ---- - - - - - - - - - - - - - - - - - -
pgmodule.cthe C Python module (_pg)
pg.pythe "classic" PyGreSQL module
pgdb.pyDB-SIG DB-API 2.0 compliant API wrapper for PygreSQL
docs/

documentation directory

-

Contains: readme.txt, announce.txt, install.txt, -changelog.txt, future.txt, pg.txt and pgdb.txt.

-

All text files are in ReST format, so HTML versions -can be easily created with buildhtml.py from docutils.

-
tutorial/

demos directory

-

Contains: basics.py, syscat.py, advanced.py and func.py.

-

The samples here have been taken from the -PostgreSQL manual and were used for module testing. -They demonstrate some PostgreSQL features.

-
-
-
-

Installation

-

You will find the installing instructions in -install.txt.

-
-
-

Information and support

- -
-

For support

-
-
Python:
-
see http://www.python.org/community/
-
PostgreSQL:
-
see http://www.postgresql.org/support/
-
PyGreSQL:
-

Contact the PyGreSQL mailing list -concerning PyGreSQL 2.0 and up.

-

If you would like to proposes changes, please join the -PyGreSQL mailing list and send context diffs there.

-

See http://mailman.vex.net/mailman/listinfo/pygresql -to join the mailing list.

-
-
-

Please note that messages to individual developers will generally not be -answered directly. All questions, comments and code changes must be -submitted to the mailing list for peer review and archiving.

-
-
-

PyGreSQL programming information

-

You may either choose to use the "classic" PyGreSQL interface -provided by the pg module or else the newer DB-API 2.0 -compliant interface provided by the pgdb module.

-

DB-API 2.0 -(Python Database API Specification v2.0) -is a specification for connecting to databases (not only PostGreSQL) -from Python that has been developed by the Python DB-SIG in 1999.

-

The programming information is available in the files -pg.txt and pgdb.txt.

-

Note that PyGreSQL is not thread-safe on the connection level. Therefore -we recommend using DBUtils <http://www.webwareforpython.org/DBUtils> -for multi-threaded environments, which supports both PyGreSQL interfaces.

-
-
-
-

ChangeLog and Future

-

The ChangeLog with past changes is in the file -changelog.txt.

-

A to do list and wish list is in the file -future.txt.

-
-
- - diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/readme.txt b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/readme.txt deleted file mode 100644 index 77ed8efd909b..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/docs/readme.txt +++ /dev/null @@ -1,206 +0,0 @@ -========================================== -PyGreSQL - Python interface for PostgreSQL -========================================== - --------------------- -PyGreSQL version 4.0 --------------------- - -.. meta:: - :description: PyGreSQL - Python interface for PostgreSQL - :keywords: PyGreSQL, PostGreSQL, Python - -.. contents:: Contents - - -Copyright notice -================ - -Written by D'Arcy J.M. Cain (darcy@druid.net) - -Based heavily on code written by Pascal Andre (andre@chimay.via.ecp.fr) - -Copyright (c) 1995, Pascal Andre - -Further modifications copyright (c) 1997-2008 by D'Arcy J.M. Cain -(darcy@druid.net) - -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 or in any -new file that contains a substantial portion of this file. - -IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE -AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE -AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, -ENHANCEMENTS, OR MODIFICATIONS. - - -Introduction -============ - -**PostgreSQL** is a highly scalable, SQL compliant, open source -object-relational database management system. With more than 15 years -of development history, it is quickly becoming the de facto database -for enterprise level open source solutions. -Best of all, PostgreSQL's source code is available under the most liberal -open source license: the BSD license. - -**Python** Python is an interpreted, interactive, object-oriented -programming language. It is often compared to Tcl, Perl, Scheme or Java. -Python combines remarkable power with very clear syntax. It has modules, -classes, exceptions, very high level dynamic data types, and dynamic typing. -There are interfaces to many system calls and libraries, as well as to -various windowing systems (X11, Motif, Tk, Mac, MFC). New built-in modules -are easily written in C or C++. Python is also usable as an extension -language for applications that need a programmable interface. -The Python implementation is copyrighted but freely usable and distributable, -even for commercial use. - -**PyGreSQL** is a Python module that interfaces to a PostgreSQL database. -It embeds the PostgreSQL query library to allow easy use of the powerful -PostgreSQL features from a Python script. - -PyGreSQL is developed and tested on a NetBSD system, but it should also -run on most other platforms where PostgreSQL and Python is running. -It is based on the PyGres95 code written by Pascal Andre (andre@chimay.via.ecp.fr). -D'Arcy (darcy@druid.net) renamed it to PyGreSQL starting with -version 2.0 and serves as the "BDFL" of PyGreSQL. - -The current version PyGreSQL 4.0 needs PostgreSQL 7.2 and Python 2.3 or above. - - -Where to get ... ? -================== - -Home sites of the different packages ------------------------------------- -**Python**: - http://www.python.org - -**PostgreSQL**: - http://www.postgresql.org - -**PyGreSQL**: - http://www.pygresql.org - -Download PyGreSQL here ----------------------- -The **released version of the source code** is available at - * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL.tgz -You can also check the latest **pre-release version** at - * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL-beta.tgz -A **Linux RPM** can be picked up from - * ftp://ftp.pygresql.org/pub/distrib/pygresql.i386.rpm -A **NetBSD package** is available in their pkgsrc collection - * ftp://ftp.netbsd.org/pub/NetBSD/packages/pkgsrc/databases/py-postgresql/README.html -A **FreeBSD package** is available in their ports collection - * http://www.freebsd.org/cgi/cvsweb.cgi/ports/databases/py-PyGreSQL/ -A **Win32 package** for various Python versions is available at - * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL-4.0.win32-py2.3.exe - * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL-4.0.win32-py2.4.exe - * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL-4.0.win32-py2.5.exe - * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL-4.0.win32-py2.6.exe -You can also find PyGreSQL on the **Python Package Index** at - * http://pypi.python.org/pypi/PyGreSQL/ - - -Distribution files -================== - -========== = -pgmodule.c the C Python module (_pg) -pg.py the "classic" PyGreSQL module -pgdb.py DB-SIG DB-API 2.0 compliant API wrapper for PygreSQL -docs/ documentation directory - - Contains: readme.txt, announce.txt, install.txt, - changelog.txt, future.txt, pg.txt and pgdb.txt. - - All text files are in ReST format, so HTML versions - can be easily created with buildhtml.py from docutils. -tutorial/ demos directory - - Contains: basics.py, syscat.py, advanced.py and func.py. - - The samples here have been taken from the - PostgreSQL manual and were used for module testing. - They demonstrate some PostgreSQL features. -========== = - - -Installation -============ -You will find the installing instructions in -`install.txt `_. - - -Information and support -======================= - -For general information ------------------------ -**Python**: - http://www.python.org - -**PostgreSQL**: - http://www.postgresql.org - -**PyGreSQL**: - http://www.pygresql.org - -For support ------------ -**Python**: - see http://www.python.org/community/ - -**PostgreSQL**: - see http://www.postgresql.org/support/ - -**PyGreSQL**: - Contact the PyGreSQL mailing list - concerning PyGreSQL 2.0 and up. - - If you would like to proposes changes, please join the - PyGreSQL mailing list and send context diffs there. - - See http://mailman.vex.net/mailman/listinfo/pygresql - to join the mailing list. - -Please note that messages to individual developers will generally not be -answered directly. All questions, comments and code changes must be -submitted to the mailing list for peer review and archiving. - -PyGreSQL programming information --------------------------------- -You may either choose to use the "classic" PyGreSQL interface -provided by the `pg` module or else the newer DB-API 2.0 -compliant interface provided by the `pgdb` module. - -`DB-API 2.0 `_ -(Python Database API Specification v2.0) -is a specification for connecting to databases (not only PostGreSQL) -from Python that has been developed by the Python DB-SIG in 1999. - -The programming information is available in the files -`pg.txt `_ and `pgdb.txt `_. - -Note that PyGreSQL is not thread-safe on the connection level. Therefore -we recommend using `DBUtils ` -for multi-threaded environments, which supports both PyGreSQL interfaces. - - -ChangeLog and Future -==================== -The ChangeLog with past changes is in the file -`changelog.txt `_. - -A to do list and wish list is in the file -`future.txt `_. diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pg.py b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pg.py deleted file mode 100644 index 53c666912c9e..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pg.py +++ /dev/null @@ -1,711 +0,0 @@ -#!/usr/bin/env python -# -# pg.py -# -# Written by D'Arcy J.M. Cain -# Improved by Christoph Zwerschke -# -# $Id: pg.py,v 1.77 2008/12/30 16:40:00 darcy Exp $ -# - -"""PyGreSQL classic interface. - -This pg module implements some basic database management stuff. -It includes the _pg module and builds on it, providing the higher -level wrapper class named DB with addtional functionality. -This is known as the "classic" ("old style") PyGreSQL interface. -For a DB-API 2 compliant interface use the newer pgdb module. - -""" - -from _pg import * -try: - frozenset -except NameError: # Python < 2.4 - from sets import ImmutableSet as frozenset -try: - from decimal import Decimal - set_decimal(Decimal) -except ImportError: - pass # Python < 2.4 - - -# Auxiliary functions which are independent from a DB connection: - -def _is_quoted(s): - """Check whether this string is a quoted identifier.""" - s = s.replace('_', 'a') - return not s.isalnum() or s[:1].isdigit() or s != s.lower() - -def _is_unquoted(s): - """Check whether this string is an unquoted identifier.""" - s = s.replace('_', 'a') - return s.isalnum() and not s[:1].isdigit() - -def _split_first_part(s): - """Split the first part of a dot separated string.""" - s = s.lstrip() - if s[:1] == '"': - p = [] - s = s.split('"', 3)[1:] - p.append(s[0]) - while len(s) == 3 and s[1] == '': - p.append('"') - s = s[2].split('"', 2) - p.append(s[0]) - p = [''.join(p)] - s = '"'.join(s[1:]).lstrip() - if s: - if s[:0] == '.': - p.append(s[1:]) - else: - s = _split_first_part(s) - p[0] += s[0] - if len(s) > 1: - p.append(s[1]) - else: - p = s.split('.', 1) - s = p[0].rstrip() - if _is_unquoted(s): - s = s.lower() - p[0] = s - return p - -def _split_parts(s): - """Split all parts of a dot separated string.""" - q = [] - while s: - s = _split_first_part(s) - q.append(s[0]) - if len(s) < 2: - break - s = s[1] - return q - -def _join_parts(s): - """Join all parts of a dot separated string.""" - return '.'.join([_is_quoted(p) and '"%s"' % p or p for p in s]) - -def _oid_key(qcl): - """Build oid key from qualified class name.""" - return 'oid(%s)' % qcl - - -# The PostGreSQL database connection interface: - -class DB(object): - """Wrapper class for the _pg connection type.""" - - def __init__(self, *args, **kw): - """Create a new connection. - - You can pass either the connection parameters or an existing - _pg or pgdb connection. This allows you to use the methods - of the classic pg interface with a DB-API 2 pgdb connection. - - """ - if not args and len(kw) == 1: - db = kw.get('db') - elif not kw and len(args) == 1: - db = args[0] - else: - db = None - if db: - if isinstance(db, DB): - db = db.db - else: - try: - db = db._cnx - except AttributeError: - pass - if not db or not hasattr(db, 'db') or not hasattr(db, 'query'): - db = connect(*args, **kw) - self._closeable = 1 - else: - self._closeable = 0 - self.db = db - self.dbname = db.db - self._attnames = {} - self._pkeys = {} - self._privileges = {} - self._args = args, kw - self.debug = None # For debugging scripts, this can be set - # * to a string format specification (e.g. in CGI set to "%s
"), - # * to a file object to write debug statements or - # * to a callable object which takes a string argument. - - def __getattr__(self, name): - # All undefined members are the same as in the underlying pg connection: - if self.db: - return getattr(self.db, name) - else: - raise InternalError('Connection is not valid') - - # Auxiliary methods - - def _do_debug(self, s): - """Print a debug message.""" - if self.debug: - if isinstance(self.debug, basestring): - print self.debug % s - elif isinstance(self.debug, file): - print >> self.debug, s - elif callable(self.debug): - self.debug(s) - - def _quote_text(self, d): - """Quote text value.""" - if not isinstance(d, basestring): - d = str(d) - return "'%s'" % self.escape_string(d) - - _bool_true = frozenset('t true 1 y yes on'.split()) - - def _quote_bool(self, d): - """Quote boolean value.""" - if isinstance(d, basestring): - if not d: - return 'NULL' - d = d.lower() in self._bool_true - else: - d = bool(d) - return ("'f'", "'t'")[d] - - _date_literals = frozenset('current_date current_time' - ' current_timestamp localtime localtimestamp'.split()) - - def _quote_date(self, d): - """Quote date value.""" - if not d: - return 'NULL' - if isinstance(d, basestring) and d.lower() in self._date_literals: - return d - return self._quote_text(d) - - def _quote_num(self, d): - """Quote numeric value.""" - if not d and d != 0: - return 'NULL' - return str(d) - - def _quote_money(self, d): - """Quote money value.""" - if not d: - return 'NULL' - return "'%.2f'" % float(d) - - _quote_funcs = dict( # quote methods for each type - text=_quote_text, bool=_quote_bool, date=_quote_date, - int=_quote_num, num=_quote_num, float=_quote_num, - money=_quote_money) - - def _quote(self, d, t): - """Return quotes if needed.""" - if d is None: - return 'NULL' - try: - quote_func = self._quote_funcs[t] - except KeyError: - quote_func = self._quote_funcs['text'] - return quote_func(self, d) - - def _split_schema(self, cl): - """Return schema and name of object separately. - - This auxiliary function splits off the namespace (schema) - belonging to the class with the name cl. If the class name - is not qualified, the function is able to determine the schema - of the class, taking into account the current search path. - - """ - s = _split_parts(cl) - if len(s) > 1: # name already qualfied? - # should be database.schema.table or schema.table - if len(s) > 3: - raise ProgrammingError('Too many dots in class name %s' % cl) - schema, cl = s[-2:] - else: - cl = s[0] - # determine search path - q = 'SELECT current_schemas(TRUE)' - schemas = self.db.query(q).getresult()[0][0][1:-1].split(',') - if schemas: # non-empty path - # search schema for this object in the current search path - q = ' UNION '.join( - ["SELECT %d::integer AS n, '%s'::name AS nspname" - % s for s in enumerate(schemas)]) - q = ("SELECT nspname FROM pg_class" - " JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid" - " JOIN (%s) AS p USING (nspname)" - " WHERE pg_class.relname = '%s'" - " ORDER BY n LIMIT 1" % (q, cl)) - schema = self.db.query(q).getresult() - if schema: # schema found - schema = schema[0][0] - else: # object not found in current search path - schema = 'public' - else: # empty path - schema = 'public' - return schema, cl - - def _add_schema(self, cl): - """Ensure that the class name is prefixed with a schema name.""" - return _join_parts(self._split_schema(cl)) - - # Public methods - - # escape_string and escape_bytea exist as methods, - # so we define unescape_bytea as a method as well - unescape_bytea = staticmethod(unescape_bytea) - - def close(self): - """Close the database connection.""" - # Wraps shared library function so we can track state. - if self._closeable: - if self.db: - self.db.close() - self.db = None - else: - raise InternalError('Connection already closed') - - def reset(self): - """Reset connection with current parameters. - - All derived queries and large objects derived from this connection - will not be usable after this call. - - """ - self.db.reset() - - def reopen(self): - """Reopen connection to the database. - - Used in case we need another connection to the same database. - Note that we can still reopen a database that we have closed. - - """ - # There is no such shared library function. - if self._closeable: - db = connect(*self._args[0], **self._args[1]) - if self.db: - self.db.close() - self.db = db - - def query(self, qstr): - """Executes a SQL command string. - - This method simply sends a SQL query to the database. If the query is - an insert statement that inserted exactly one row into a table that - has OIDs, the return value is the OID of the newly inserted row. - If the query is an update or delete statement, or an insert statement - that did not insert exactly one row in a table with OIDs, then the - numer of rows affected is returned as a string. If it is a statement - that returns rows as a result (usually a select statement, but maybe - also an "insert/update ... returning" statement), this method returns - a pgqueryobject that can be accessed via getresult() or dictresult() - or simply printed. Otherwise, it returns `None`. - - """ - # Wraps shared library function for debugging. - if not self.db: - raise InternalError('Connection is not valid') - self._do_debug(qstr) - return self.db.query(qstr) - - def pkey(self, cl, newpkey=None): - """This method gets or sets the primary key of a class. - - Composite primary keys are represented as frozensets. Note that - this raises an exception if the table does not have a primary key. - - If newpkey is set and is not a dictionary then set that - value as the primary key of the class. If it is a dictionary - then replace the _pkeys dictionary with a copy of it. - - """ - # First see if the caller is supplying a dictionary - if isinstance(newpkey, dict): - # make sure that all classes have a namespace - self._pkeys = dict([ - ('.' in cl and cl or 'public.' + cl, pkey) - for cl, pkey in newpkey.iteritems()]) - return self._pkeys - - qcl = self._add_schema(cl) # build fully qualified class name - # Check if the caller is supplying a new primary key for the class - if newpkey: - self._pkeys[qcl] = newpkey - return newpkey - - # Get all the primary keys at once - if qcl not in self._pkeys: - # if not found, check again in case it was added after we started - self._pkeys = {} - if self.server_version >= 80200: - # the ANY syntax works correctly only with PostgreSQL >= 8.2 - any_indkey = "= ANY (pg_index.indkey)" - else: - any_indkey = "IN (%s)" % ', '.join( - ['pg_index.indkey[%d]' % i for i in range(16)]) - for r in self.db.query( - "SELECT pg_namespace.nspname, pg_class.relname," - " pg_attribute.attname FROM pg_class" - " JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace" - " AND pg_namespace.nspname NOT LIKE 'pg_%'" - " JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid" - " AND pg_attribute.attisdropped = 'f'" - " JOIN pg_index ON pg_index.indrelid = pg_class.oid" - " AND pg_index.indisprimary = 't'" - " AND pg_attribute.attnum " + any_indkey).getresult(): - cl, pkey = _join_parts(r[:2]), r[2] - self._pkeys.setdefault(cl, []).append(pkey) - # (only) for composite primary keys, the values will be frozensets - for cl, pkey in self._pkeys.iteritems(): - self._pkeys[cl] = len(pkey) > 1 and frozenset(pkey) or pkey[0] - self._do_debug(self._pkeys) - - # will raise an exception if primary key doesn't exist - return self._pkeys[qcl] - - def get_databases(self): - """Get list of databases in the system.""" - return [s[0] for s in - self.db.query('SELECT datname FROM pg_database').getresult()] - - def get_relations(self, kinds=None): - """Get list of relations in connected database of specified kinds. - - If kinds is None or empty, all kinds of relations are returned. - Otherwise kinds can be a string or sequence of type letters - specifying which kind of relations you want to list. - - """ - where = kinds and "pg_class.relkind IN (%s) AND" % ','.join( - ["'%s'" % x for x in kinds]) or '' - return map(_join_parts, self.db.query( - "SELECT pg_namespace.nspname, pg_class.relname " - "FROM pg_class " - "JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace " - "WHERE %s pg_class.relname !~ '^Inv' AND " - "pg_class.relname !~ '^pg_' " - "ORDER BY 1, 2" % where).getresult()) - - def get_tables(self): - """Return list of tables in connected database.""" - return self.get_relations('r') - - def get_attnames(self, cl, newattnames=None): - """Given the name of a table, digs out the set of attribute names. - - Returns a dictionary of attribute names (the names are the keys, - the values are the names of the attributes' types). - If the optional newattnames exists, it must be a dictionary and - will become the new attribute names dictionary. - - """ - if isinstance(newattnames, dict): - self._attnames = newattnames - return - elif newattnames: - raise ProgrammingError( - 'If supplied, newattnames must be a dictionary') - cl = self._split_schema(cl) # split into schema and class - qcl = _join_parts(cl) # build fully qualified name - # May as well cache them: - if qcl in self._attnames: - return self._attnames[qcl] - if qcl not in self.get_relations('rv'): - raise ProgrammingError('Class %s does not exist' % qcl) - t = {} - for att, typ in self.db.query("SELECT pg_attribute.attname," - " pg_type.typname FROM pg_class" - " JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid" - " JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid" - " JOIN pg_type ON pg_type.oid = pg_attribute.atttypid" - " WHERE pg_namespace.nspname = '%s' AND pg_class.relname = '%s'" - " AND (pg_attribute.attnum > 0 or pg_attribute.attname = 'oid')" - " AND pg_attribute.attisdropped = 'f'" - % cl).getresult(): - if typ.startswith('bool'): - t[att] = 'bool' - elif typ.startswith('abstime'): - t[att] = 'date' - elif typ.startswith('date'): - t[att] = 'date' - elif typ.startswith('interval'): - t[att] = 'date' - elif typ.startswith('timestamp'): - t[att] = 'date' - elif typ.startswith('oid'): - t[att] = 'int' - elif typ.startswith('int'): - t[att] = 'int' - elif typ.startswith('float'): - t[att] = 'float' - elif typ.startswith('numeric'): - t[att] = 'num' - elif typ.startswith('money'): - t[att] = 'money' - else: - t[att] = 'text' - self._attnames[qcl] = t # cache it - return self._attnames[qcl] - - def has_table_privilege(self, cl, privilege='select'): - """Check whether current user has specified table privilege.""" - qcl = self._add_schema(cl) - privilege = privilege.lower() - try: - return self._privileges[(qcl, privilege)] - except KeyError: - q = "SELECT has_table_privilege('%s', '%s')" % (qcl, privilege) - ret = self.db.query(q).getresult()[0][0] == 't' - self._privileges[(qcl, privilege)] = ret - return ret - - def get(self, cl, arg, keyname=None): - """Get a tuple from a database table or view. - - This method is the basic mechanism to get a single row. The keyname - that the key specifies a unique row. If keyname is not specified - then the primary key for the table is used. If arg is a dictionary - then the value for the key is taken from it and it is modified to - include the new values, replacing existing values where necessary. - For a composite key, keyname can also be a sequence of key names. - The OID is also put into the dictionary if the table has one, but - in order to allow the caller to work with multiple tables, it is - munged as oid(schema.table). - - """ - if cl.endswith('*'): # scan descendant tables? - cl = cl[:-1].rstrip() # need parent table name - # build qualified class name - qcl = self._add_schema(cl) - # To allow users to work with multiple tables, - # we munge the name of the "oid" the key - qoid = _oid_key(qcl) - if not keyname: - # use the primary key by default - try: - keyname = self.pkey(qcl) - except KeyError: - raise ProgrammingError('Class %s has no primary key' % qcl) - # We want the oid for later updates if that isn't the key - if keyname == 'oid': - if isinstance(arg, dict): - if qoid not in arg: - raise ProgrammingError('%s not in arg' % qoid) - else: - arg = {qoid: arg} - where = 'oid = %s' % arg[qoid] - attnames = '*' - else: - attnames = self.get_attnames(qcl) - if isinstance(keyname, basestring): - keyname = (keyname,) - if not isinstance(arg, dict): - if len(keyname) > 1: - raise ProgrammingError('Composite key needs dict as arg') - arg = dict([(k, arg) for k in keyname]) - where = ' AND '.join(['%s = %s' - % (k, self._quote(arg[k], attnames[k])) for k in keyname]) - attnames = ', '.join(attnames) - q = 'SELECT %s FROM %s WHERE %s LIMIT 1' % (attnames, qcl, where) - self._do_debug(q) - res = self.db.query(q).dictresult() - if not res: - raise DatabaseError('No such record in %s where %s' % (qcl, where)) - for att, value in res[0].iteritems(): - arg[att == 'oid' and qoid or att] = value - return arg - - def insert(self, cl, d=None, **kw): - """Insert a tuple into a database table. - - This method inserts a row into a table. If a dictionary is - supplied it starts with that. Otherwise it uses a blank dictionary. - Either way the dictionary is updated from the keywords. - - The dictionary is then, if possible, reloaded with the values actually - inserted in order to pick up values modified by rules, triggers, etc. - - Note: The method currently doesn't support insert into views - although PostgreSQL does. - - """ - qcl = self._add_schema(cl) - qoid = _oid_key(qcl) - if d is None: - d = {} - d.update(kw) - attnames = self.get_attnames(qcl) - names, values = [], [] - for n in attnames: - if n != 'oid' and n in d: - names.append('"%s"' % n) - values.append(self._quote(d[n], attnames[n])) - names, values = ', '.join(names), ', '.join(values) - selectable = self.has_table_privilege(qcl) - if selectable and self.server_version >= 80200: - ret = ' RETURNING %s*' % ('oid' in attnames and 'oid, ' or '') - else: - ret = '' - q = 'INSERT INTO %s (%s) VALUES (%s)%s' % (qcl, names, values, ret) - self._do_debug(q) - res = self.db.query(q) - if ret: - res = res.dictresult() - for att, value in res[0].iteritems(): - d[att == 'oid' and qoid or att] = value - elif isinstance(res, int): - d[qoid] = res - if selectable: - self.get(qcl, d, 'oid') - elif selectable: - if qoid in d: - self.get(qcl, d, 'oid') - else: - try: - self.get(qcl, d) - except ProgrammingError: - pass # table has no primary key - return d - - def update(self, cl, d=None, **kw): - """Update an existing row in a database table. - - Similar to insert but updates an existing row. The update is based - on the OID value as munged by get or passed as keyword, or on the - primary key of the table. The dictionary is modified, if possible, - to reflect any changes caused by the update due to triggers, rules, - default values, etc. - - """ - # Update always works on the oid which get returns if available, - # otherwise use the primary key. Fail if neither. - # Note that we only accept oid key from named args for safety - qcl = self._add_schema(cl) - qoid = _oid_key(qcl) - if 'oid' in kw: - kw[qoid] = kw['oid'] - del kw['oid'] - if d is None: - d = {} - d.update(kw) - attnames = self.get_attnames(qcl) - if qoid in d: - where = 'oid = %s' % d[qoid] - keyname = () - else: - try: - keyname = self.pkey(qcl) - except KeyError: - raise ProgrammingError('Class %s has no primary key' % qcl) - if isinstance(keyname, basestring): - keyname = (keyname,) - try: - where = ' AND '.join(['%s = %s' - % (k, self._quote(d[k], attnames[k])) for k in keyname]) - except KeyError: - raise ProgrammingError('Update needs primary key or oid.') - values = [] - for n in attnames: - if n in d and n not in keyname: - values.append('%s = %s' % (n, self._quote(d[n], attnames[n]))) - if not values: - return d - values = ', '.join(values) - selectable = self.has_table_privilege(qcl) - if selectable and self.server_version >= 880200: - ret = ' RETURNING %s*' % ('oid' in attnames and 'oid, ' or '') - else: - ret = '' - q = 'UPDATE %s SET %s WHERE %s%s' % (qcl, values, where, ret) - self._do_debug(q) - res = self.db.query(q) - if ret: - res = self.db.query(q).dictresult() - for att, value in res[0].iteritems(): - d[att == 'oid' and qoid or att] = value - else: - self.db.query(q) - if selectable: - if qoid in d: - self.get(qcl, d, 'oid') - else: - self.get(qcl, d) - return d - - def clear(self, cl, a=None): - """ - - This method clears all the attributes to values determined by the types. - Numeric types are set to 0, Booleans are set to 'f', and everything - else is set to the empty string. If the array argument is present, - it is used as the array and any entries matching attribute names are - cleared with everything else left unchanged. - - """ - # At some point we will need a way to get defaults from a table. - qcl = self._add_schema(cl) - if a is None: - a = {} # empty if argument is not present - attnames = self.get_attnames(qcl) - for n, t in attnames.iteritems(): - if n == 'oid': - continue - if t in ('int', 'float', 'num', 'money'): - a[n] = 0 - elif t == 'bool': - a[n] = 'f' - else: - a[n] = '' - return a - - def delete(self, cl, d=None, **kw): - """Delete an existing row in a database table. - - This method deletes the row from a table. It deletes based on the - OID value as munged by get or passed as keyword, or on the primary - key of the table. The return value is the number of deleted rows - (i.e. 0 if the row did not exist and 1 if the row was deleted). - - """ - # Like update, delete works on the oid. - # One day we will be testing that the record to be deleted - # isn't referenced somewhere (or else PostgreSQL will). - # Note that we only accept oid key from named args for safety - qcl = self._add_schema(cl) - qoid = _oid_key(qcl) - if 'oid' in kw: - kw[qoid] = kw['oid'] - del kw['oid'] - if d is None: - d = {} - d.update(kw) - if qoid in d: - where = 'oid = %s' % d[qoid] - else: - try: - keyname = self.pkey(qcl) - except KeyError: - raise ProgrammingError('Class %s has no primary key' % qcl) - if isinstance(keyname, basestring): - keyname = (keyname,) - attnames = self.get_attnames(qcl) - try: - where = ' AND '.join(['%s = %s' - % (k, self._quote(d[k], attnames[k])) for k in keyname]) - except KeyError: - raise ProgrammingError('Delete needs primary key or oid.') - q = 'DELETE FROM %s WHERE %s' % (qcl, where) - self._do_debug(q) - return int(self.db.query(q)) - - -# if run as script, print some information - -if __name__ == '__main__': - print 'PyGreSQL version', version - print - print __doc__ diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pgdb.py b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pgdb.py deleted file mode 100644 index 4b4344c971d6..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pgdb.py +++ /dev/null @@ -1,582 +0,0 @@ -#!/usr/bin/env python -# -# pgdb.py -# -# Written by D'Arcy J.M. Cain -# -# $Id: pgdb.py,v 1.54 2008/11/23 14:32:18 cito Exp $ -# - -"""pgdb - DB-API 2.0 compliant module for PygreSQL. - -(c) 1999, Pascal Andre . -See package documentation for further information on copyright. - -Inline documentation is sparse. -See DB-API 2.0 specification for usage information: -http://www.python.org/peps/pep-0249.html - -Basic usage: - - pgdb.connect(connect_string) # open a connection - # connect_string = 'host:database:user:password:opt:tty' - # All parts are optional. You may also pass host through - # password as keyword arguments. To pass a port, - # pass it in the host keyword parameter: - pgdb.connect(host='localhost:5432') - - connection.cursor() # open a cursor - - cursor.execute(query[, params]) - # Execute a query, binding params (a dictionary) if they are - # passed. The binding syntax is the same as the % operator - # for dictionaries, and no quoting is done. - - cursor.executemany(query, list of params) - # Execute a query many times, binding each param dictionary - # from the list. - - cursor.fetchone() # fetch one row, [value, value, ...] - - cursor.fetchall() # fetch all rows, [[value, value, ...], ...] - - cursor.fetchmany([size]) - # returns size or cursor.arraysize number of rows, - # [[value, value, ...], ...] from result set. - # Default cursor.arraysize is 1. - - cursor.description # returns information about the columns - # [(column_name, type_name, display_size, - # internal_size, precision, scale, null_ok), ...] - # Note that precision, scale and null_ok are not implemented. - - cursor.rowcount # number of rows available in the result set - # Available after a call to execute. - - connection.commit() # commit transaction - - connection.rollback() # or rollback transaction - - cursor.close() # close the cursor - - connection.close() # close the connection - -""" - -from _pg import * -import time -try: - frozenset -except NameError: # Python < 2.4 - from sets import ImmutableSet as frozenset -from datetime import datetime, timedelta -try: # use Decimal if available - from decimal import Decimal - set_decimal(Decimal) -except ImportError: # otherwise (Python < 2.4) - Decimal = float # use float instead of Decimal - - -### Module Constants - -# compliant with DB SIG 2.0 -apilevel = '2.0' - -# module may be shared, but not connections -threadsafety = 1 - -# this module use extended python format codes -paramstyle = 'pyformat' - - -### Internal Types Handling - -def decimal_type(decimal_type=None): - """Get or set global type to be used for decimal values.""" - global Decimal - if decimal_type is not None: - Decimal = decimal_type - set_decimal(decimal_type) - return Decimal - - -def _cast_bool(value): - return value[:1] in ['t', 'T'] - - -def _cast_money(value): - return Decimal(''.join(filter( - lambda v: v in '0123456789.-', value))) - - -_cast = {'bool': _cast_bool, - 'int2': int, 'int4': int, 'serial': int, - 'int8': long, 'oid': long, 'oid8': long, - 'float4': float, 'float8': float, - 'numeric': Decimal, 'money': _cast_money} - - -class pgdbTypeCache(dict): - """Cache for database types.""" - - def __init__(self, cnx): - """Initialize type cache for connection.""" - super(pgdbTypeCache, self).__init__() - self._src = cnx.source() - - def typecast(typ, value): - """Cast value to database type.""" - if value is None: - # for NULL values, no typecast is necessary - return None - cast = _cast.get(typ) - if cast is None: - # no typecast available or necessary - return value - else: - return cast(value) - typecast = staticmethod(typecast) - - def getdescr(self, oid): - """Get name of database type with given oid.""" - try: - return self[oid] - except KeyError: - self._src.execute( - "SELECT typname, typlen " - "FROM pg_type WHERE oid=%s" % oid) - res = self._src.fetch(1)[0] - # The column name is omitted from the return value. - # It will have to be prepended by the caller. - res = (res[0], None, int(res[1]), - None, None, None) - self[oid] = res - return res - - -class _quotedict(dict): - """Dictionary with auto quoting of its items. - - The quote attribute must be set to the desired quote function. - - """ - - def __getitem__(self, key): - return self.quote(super(_quotedict, self).__getitem__(key)) - - -### Cursor Object - -class pgdbCursor(object): - """Cursor Object.""" - - def __init__(self, dbcnx): - """Create a cursor object for the database connection.""" - self.connection = self._dbcnx = dbcnx - self._cnx = dbcnx._cnx - self._type_cache = dbcnx._type_cache - self._src = self._cnx.source() - self.description = None - self.rowcount = -1 - self.arraysize = 1 - self.lastrowid = None - - def __iter__(self): - """Return self to make cursors compatible to the iteration protocol.""" - return self - - def _quote(self, val): - """Quote value depending on its type.""" - if isinstance(val, datetime): - val = str(val) - elif isinstance(val, unicode): - val = val.encode( 'utf8' ) - if isinstance(val, str): - val = "'%s'" % self._cnx.escape_string(val) - elif isinstance(val, (int, long, float)): - pass - elif val is None: - val = 'NULL' - elif isinstance(val, (list, tuple)): - val = '(%s)' % ','.join(map(lambda v: str(self._quote(v)), val)) - elif Decimal is not float and isinstance(val, Decimal): - pass - elif hasattr(val, '__pg_repr__'): - val = val.__pg_repr__() - else: - raise InterfaceError( - 'do not know how to handle type %s' % type(val)) - return val - - def _quoteparams(self, string, params): - """Quote parameters. - - This function works for both mappings and sequences. - - """ - if isinstance(params, dict): - params = _quotedict(params) - params.quote = self._quote - else: - params = tuple(map(self._quote, params)) - return string % params - - def row_factory(row): - """Process rows before they are returned. - - You can overwrite this with a custom row factory, - e.g. a dict factory: - - class myCursor(pgdb.pgdbCursor): - def cursor.row_factory(self, row): - d = {} - for idx, col in enumerate(self.description): - d[col[0]] = row[idx] - return d - cursor = myCursor(cnx) - - """ - return row - row_factory = staticmethod(row_factory) - - def close(self): - """Close the cursor object.""" - self._src.close() - self.description = None - self.rowcount = -1 - self.lastrowid = None - - def execute(self, operation, params=None): - """Prepare and execute a database operation (query or command).""" - # The parameters may also be specified as list of - # tuples to e.g. insert multiple rows in a single - # operation, but this kind of usage is deprecated: - if (params and isinstance(params, list) - and isinstance(params[0], tuple)): - self.executemany(operation, params) - else: - # not a list of tuples - self.executemany(operation, (params,)) - - def executemany(self, operation, param_seq): - """Prepare operation and execute it against a parameter sequence.""" - if not param_seq: - # don't do anything without parameters - return - self.description = None - self.rowcount = -1 - # first try to execute all queries - totrows = 0 - sql = "BEGIN" - try: - if not self._dbcnx._tnx: - try: - self._cnx.source().execute(sql) - except Exception: - raise OperationalError("can't start transaction") - self._dbcnx._tnx = True - for params in param_seq: - if params: - sql = self._quoteparams(operation, params) - else: - sql = operation - rows = self._src.execute(sql) - if rows: # true if not DML - totrows += rows - else: - self.rowcount = -1 - except Error, msg: - raise DatabaseError("error '%s' in '%s'" % (msg, sql)) - except Exception, err: - raise OperationalError("internal error in '%s': %s" % (sql, err)) - # then initialize result raw count and description - if self._src.resulttype == RESULT_DQL: - self.rowcount = self._src.ntuples - getdescr = self._type_cache.getdescr - coltypes = self._src.listinfo() - self.description = [typ[1:2] + getdescr(typ[2]) for typ in coltypes] - self.lastrowid = self._src.oidstatus() - else: - self.rowcount = totrows - self.description = None - self.lastrowid = self._src.oidstatus() - - def fetchone(self): - """Fetch the next row of a query result set.""" - res = self.fetchmany(1, False) - try: - return res[0] - except IndexError: - return None - - def fetchall(self): - """Fetch all (remaining) rows of a query result.""" - return self.fetchmany(-1, False) - - def fetchmany(self, size=None, keep=False): - """Fetch the next set of rows of a query result. - - The number of rows to fetch per call is specified by the - size parameter. If it is not given, the cursor's arraysize - determines the number of rows to be fetched. If you set - the keep parameter to true, this is kept as new arraysize. - - """ - if size is None: - size = self.arraysize - if keep: - self.arraysize = size - try: - result = self._src.fetch(size) - except Error, err: - raise DatabaseError(str(err)) - row_factory = self.row_factory - typecast = self._type_cache.typecast - coltypes = [desc[1] for desc in self.description] - return [row_factory([typecast(*args) - for args in zip(coltypes, row)]) for row in result] - - def next(self): - """Return the next row (support for the iteration protocol).""" - res = self.fetchone() - if res is None: - raise StopIteration - return res - - def nextset(): - """Not supported.""" - raise NotSupportedError("nextset() is not supported") - nextset = staticmethod(nextset) - - def setinputsizes(sizes): - """Not supported.""" - pass - setinputsizes = staticmethod(setinputsizes) - - def setoutputsize(size, column=0): - """Not supported.""" - pass - setoutputsize = staticmethod(setoutputsize) - - -### Connection Objects - -class pgdbCnx(object): - """Connection Object.""" - - # expose the exceptions as attributes on the connection object - Error = Error - Warning = Warning - InterfaceError = InterfaceError - DatabaseError = DatabaseError - InternalError = InternalError - OperationalError = OperationalError - ProgrammingError = ProgrammingError - IntegrityError = IntegrityError - DataError = DataError - NotSupportedError = NotSupportedError - - def __init__(self, cnx): - """Create a database connection object.""" - self._cnx = cnx # connection - self._tnx = False # transaction state - self._type_cache = pgdbTypeCache(cnx) - try: - self._cnx.source() - except Exception: - raise OperationalError("invalid connection") - - def close(self): - """Close the connection object.""" - if self._cnx: - self._cnx.close() - self._cnx = None - else: - raise OperationalError("connection has been closed") - - def commit(self): - """Commit any pending transaction to the database.""" - if self._cnx: - if self._tnx: - self._tnx = False - try: - self._cnx.source().execute("COMMIT") - except Exception: - raise OperationalError("can't commit") - else: - raise OperationalError("connection has been closed") - - def rollback(self): - """Roll back to the start of any pending transaction.""" - if self._cnx: - if self._tnx: - self._tnx = False - try: - self._cnx.source().execute("ROLLBACK") - except Exception: - raise OperationalError("can't rollback") - else: - raise OperationalError("connection has been closed") - - def cursor(self): - """Return a new Cursor Object using the connection.""" - if self._cnx: - try: - return pgdbCursor(self) - except Exception: - raise OperationalError("invalid connection") - else: - raise OperationalError("connection has been closed") - - -### Module Interface - -_connect_ = connect - -def connect(dsn=None, - user=None, password=None, - host=None, database=None): - """Connects to a database.""" - # first get params from DSN - dbport = -1 - dbhost = "" - dbbase = "" - dbuser = "" - dbpasswd = "" - dbopt = "" - dbtty = "" - try: - params = dsn.split(":") - dbhost = params[0] - dbport = int(params[1]) - dbbase = params[2] - dbuser = params[3] - dbpasswd = params[4] - dbopt = params[5] - dbtty = params[6] - except (AttributeError, IndexError, TypeError): - pass - - # override if necessary - if user is not None: - dbuser = user - if password is not None: - dbpasswd = password - if database is not None: - dbbase = database - if host is not None: - try: - params = host.split(":") - dbhost = params[0] - dbport = int(params[1]) - except (AttributeError, IndexError, TypeError, ValueError): - pass - - # empty host is localhost - if dbhost == "": - dbhost = None - if dbuser == "": - dbuser = None - - # open the connection - cnx = _connect_(dbbase, dbhost, dbport, dbopt, - dbtty, dbuser, dbpasswd) - return pgdbCnx(cnx) - - -### Types Handling - -class pgdbType(frozenset): - """Type class for a couple of PostgreSQL data types. - - PostgreSQL is object-oriented: types are dynamic. - We must thus use type names as internal type codes. - - """ - - if frozenset.__module__ == '__builtin__': - def __new__(cls, values): - if isinstance(values, basestring): - values = values.split() - return super(pgdbType, cls).__new__(cls, values) - else: # Python < 2.4 - def __init__(self, values): - if isinstance(values, basestring): - values = values.split() - super(pgdbType, self).__init__(values) - - def __eq__(self, other): - if isinstance(other, basestring): - return other in self - else: - return super(pgdbType, self).__eq__(other) - - def __ne__(self, other): - if isinstance(other, basestring): - return other not in self - else: - return super(pgdbType, self).__ne__(other) - - -# Mandatory type objects defined by DB-API 2 specs: - -STRING = pgdbType('char bpchar name text varchar') -BINARY = pgdbType('bytea') -NUMBER = pgdbType('int2 int4 serial int8 float4 float8 numeric money') -DATETIME = pgdbType('date time timetz timestamp timestamptz datetime abstime' - ' interval tinterval timespan reltime') -ROWID = pgdbType('oid oid8') - - -# Additional type objects (more specific): - -BOOL = pgdbType('bool') -SMALLINT = pgdbType('int2') -INTEGER = pgdbType('int2 int4 int8 serial') -LONG = pgdbType('int8') -FLOAT = pgdbType('float4 float8') -NUMERIC = pgdbType('numeric') -MONEY = pgdbType('money') -DATE = pgdbType('date') -TIME = pgdbType('time timetz') -TIMESTAMP = pgdbType('timestamp timestamptz datetime abstime') -INTERVAL = pgdbType('interval tinterval timespan reltime') - - -# Mandatory type helpers defined by DB-API 2 specs: - -def Date(year, month, day): - """Construct an object holding a date value.""" - return datetime(year, month, day) - -def Time(hour, minute, second): - """Construct an object holding a time value.""" - return timedelta(hour, minute, second) - -def Timestamp(year, month, day, hour, minute, second): - """construct an object holding a time stamp value.""" - return datetime(year, month, day, hour, minute, second) - -def DateFromTicks(ticks): - """Construct an object holding a date value from the given ticks value.""" - return Date(*time.localtime(ticks)[:3]) - -def TimeFromTicks(ticks): - """construct an object holding a time value from the given ticks value.""" - return Time(*time.localtime(ticks)[3:6]) - -def TimestampFromTicks(ticks): - """construct an object holding a time stamp from the given ticks value.""" - return Timestamp(*time.localtime(ticks)[:6]) - -def Binary(value): - """construct an object capable of holding a binary (long) string value.""" - return value - - -# If run as script, print some information: - -if __name__ == '__main__': - print 'PyGreSQL version', version - print - print __doc__ diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pgmodule.c b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pgmodule.c deleted file mode 100644 index e2ff47f7825f..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/pgmodule.c +++ /dev/null @@ -1,3745 +0,0 @@ -/* - * $Id: pgmodule.c,v 1.90 2008/12/03 00:17:15 cito Exp $ - * PyGres, version 2.2 A Python interface for PostgreSQL database. Written by - * D'Arcy J.M. Cain, (darcy@druid.net). Based heavily on code written by - * Pascal Andre, andre@chimay.via.ecp.fr. Copyright (c) 1995, Pascal Andre - * (andre@via.ecp.fr). - * - * 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 or in - * any new file that contains a substantial portion of this file. - * - * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, - * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE - * AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE - * AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, - * ENHANCEMENTS, OR MODIFICATIONS. - * - * Further modifications copyright 1997, 1998, 1999 by D'Arcy J.M. Cain - * (darcy@druid.net) subject to the same terms and conditions as above. - * - */ - -/* Note: This should be linked against the same C runtime lib as Python */ - -#include "postgres.h" -#include "libpq-fe.h" -#include "libpq/libpq-fs.h" -#include "catalog/pg_type.h" - -/* these will be defined in Python.h again: */ -#undef _POSIX_C_SOURCE -#undef HAVE_STRERROR -#undef snprintf -#undef vsnprintf - -#include - -static PyObject *Error, *Warning, *InterfaceError, - *DatabaseError, *InternalError, *OperationalError, *ProgrammingError, - *IntegrityError, *DataError, *NotSupportedError; - -static const char *PyPgVersion = "4.0"; - -#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) -typedef int Py_ssize_t; -#define PY_SSIZE_T_MAX INT_MAX -#define PY_SSIZE_T_MIN INT_MIN -#endif - -/* taken from fileobject.c */ -#define BUF(v) PyString_AS_STRING((PyStringObject *)(v)) - -/* default values */ -#define MODULE_NAME "pgsql" -#define PG_ARRAYSIZE 1 - -/* flags for object validity checks */ -#define CHECK_OPEN 1 -#define CHECK_CLOSE 2 -#define CHECK_CNX 4 -#define CHECK_RESULT 8 -#define CHECK_DQL 16 - -/* query result types */ -#define RESULT_EMPTY 1 -#define RESULT_DML 2 -#define RESULT_DDL 3 -#define RESULT_DQL 4 - -/* flags for move methods */ -#define QUERY_MOVEFIRST 1 -#define QUERY_MOVELAST 2 -#define QUERY_MOVENEXT 3 -#define QUERY_MOVEPREV 4 - -/* moves names for errors */ -const char *__movename[5] = -{"", "movefirst", "movelast", "movenext", "moveprev"}; - -#define MAX_BUFFER_SIZE 8192 /* maximum transaction size */ - -#ifndef NO_DIRECT -#define DIRECT_ACCESS 1 /* enables direct access functions */ -#endif - -#ifndef NO_LARGE -#define LARGE_OBJECTS 1 /* enables large objects support */ -#endif - -#ifndef NO_DEF_VAR -#define DEFAULT_VARS 1 /* enables default variables use */ -#endif - -#ifndef NO_NOTICES -#define HANDLE_NOTICES 1 /* enables notices handling */ -#endif /* NO_NOTICES */ - -#ifndef PG_VERSION_NUM -#ifdef PQnoPasswordSupplied -#define PG_VERSION_NUM 80000 -#else -#define PG_VERSION_NUM 70400 -#endif -#endif - -/* Before 8.0, PQsetdbLogin was not thread-safe with kerberos. */ -#if PG_VERSION_NUM >= 80000 || !(defined(KRB4) || defined(KRB5)) -#define PQsetdbLoginIsThreadSafe 1 -#endif - -/* --------------------------------------------------------------------- */ - -/* MODULE GLOBAL VARIABLES */ - -#ifdef DEFAULT_VARS - -static PyObject *pg_default_host; /* default database host */ -static PyObject *pg_default_base; /* default database name */ -static PyObject *pg_default_opt; /* default connection options */ -static PyObject *pg_default_tty; /* default debug tty */ -static PyObject *pg_default_port; /* default connection port */ -static PyObject *pg_default_user; /* default username */ -static PyObject *pg_default_passwd; /* default password */ -#endif /* DEFAULT_VARS */ - -#ifdef HANDLE_NOTICES -#define MAX_BUFFERED_NOTICES 100 /* max notices to keep for each connection */ -static void notice_processor(void *arg, const char *message); -#endif /* HANDLE_NOTICES */ - -DL_EXPORT(void) init_pg(void); -int *get_type_array(PGresult *result, int nfields); - -static PyObject *decimal = NULL; /* decimal type */ - -/* --------------------------------------------------------------------- */ -/* OBJECTS DECLARATION */ - -/* pg connection object */ - -typedef struct -{ - PyObject_HEAD - int valid; /* validity flag */ - PGconn *cnx; /* PostGres connection handle */ - PGresult *last_result; /* last result content */ -#ifdef HANDLE_NOTICES - PyObject *notices; /* list of notices */ -#endif /* HANDLE_NOTICES */ -} pgobject; - -staticforward PyTypeObject PgType; - -#define is_pgobject(v) ((v)->ob_type == &PgType) - -static PyObject * -pgobject_New(void) -{ - pgobject *pgobj; - - if ((pgobj = PyObject_NEW(pgobject, &PgType)) == NULL) - return NULL; - - pgobj->valid = 1; - pgobj->last_result = NULL; - pgobj->cnx = NULL; -#ifdef HANDLE_NOTICES - pgobj->notices = NULL; -#endif /* HANDLE_NOTICES */ - return (PyObject *) pgobj; -} - -/* pg query object */ - -typedef struct -{ - PyObject_HEAD - PGresult *last_result; /* last result content */ - int result_type; /* type of previous result */ - long current_pos; /* current position in last result */ - long num_rows; /* number of (affected) rows */ -} pgqueryobject; - -staticforward PyTypeObject PgQueryType; - -#define is_pgqueryobject(v) ((v)->ob_type == &PgQueryType) - -/* pg source object */ - -typedef struct -{ - PyObject_HEAD - int valid; /* validity flag */ - pgobject *pgcnx; /* parent connection object */ - PGresult *last_result; /* last result content */ - int result_type; /* result type (DDL/DML/DQL) */ - long arraysize; /* array size for fetch method */ - int current_row; /* current selected row */ - int max_row; /* number of rows in the result */ - int num_fields; /* number of fields in each row */ -} pgsourceobject; - -staticforward PyTypeObject PgSourceType; - -#define is_pgsourceobject(v) ((v)->ob_type == &PgSourceType) - - -#ifdef LARGE_OBJECTS -/* pg large object */ - -typedef struct -{ - PyObject_HEAD - pgobject *pgcnx; /* parent connection object */ - Oid lo_oid; /* large object oid */ - int lo_fd; /* large object fd */ -} pglargeobject; - -staticforward PyTypeObject PglargeType; - -#define is_pglargeobject(v) ((v)->ob_type == &PglargeType) -#endif /* LARGE_OBJECTS */ - -/* --------------------------------------------------------------------- */ -/* INTERNAL FUNCTIONS */ - - -/* prints result (mostly useful for debugging) */ -/* Note: This is a simplified version of the Postgres function PQprint(). - * PQprint() is not used because handing over a stream from Python to - * Postgres can be problematic if they use different libs for streams. - * Also, PQprint() is considered obsolete and may be removed sometime. - */ -static void -print_result(FILE *fout, const PGresult *res) -{ - int n = PQnfields(res); - if (n > 0) - { - int i, j; - int *fieldMax = NULL; - char **fields = NULL; - const char **fieldNames; - int m = PQntuples(res); - if (!(fieldNames = (const char **) calloc(n, sizeof(char *)))) - { - fprintf(stderr, "out of memory\n"); exit(1); - } - if (!(fieldMax = (int *) calloc(n, sizeof(int)))) - { - fprintf(stderr, "out of memory\n"); exit(1); - } - for (j = 0; j < n; j++) - { - const char *s = PQfname(res, j); - fieldNames[j] = s; - fieldMax[j] = s ? strlen(s) : 0; - } - if (!(fields = (char **) calloc(n * (m + 1), sizeof(char *)))) - { - fprintf(stderr, "out of memory\n"); exit(1); - } - for (i = 0; i < m; i++) - { - for (j = 0; j < n; j++) - { - const char *val; - int len; - len = PQgetlength(res, i, j); - val = PQgetvalue(res, i, j); - if (len >= 1 && val && *val) - { - if (len > fieldMax[j]) - fieldMax[j] = len; - if (!(fields[i * n + j] = (char *) malloc(len + 1))) - { - fprintf(stderr, "out of memory\n"); exit(1); - } - strcpy(fields[i * n + j], val); - } - } - } - for (j = 0; j < n; j++) - { - const char *s = PQfname(res, j); - int len = strlen(s); - if (len > fieldMax[j]) - fieldMax[j] = len; - fprintf(fout, "%-*s", fieldMax[j], s); - if (j + 1 < n) - fputc('|', fout); - } - fputc('\n', fout); - for (j = 0; j < n; j++) - { - for (i = fieldMax[j]; i--; fputc('-', fout)); - if (j + 1 < n) - fputc('+', fout); - } - fputc('\n', fout); - for (i = 0; i < m; i++) - { - for (j = 0; j < n; j++) - { - char *s = fields[i * n + j]; - fprintf(fout, "%-*s", fieldMax[j], s ? s : ""); - if (j + 1 < n) - fputc('|', fout); - if (s) - free(s); - } - fputc('\n', fout); - } - free(fields); - fprintf(fout, "(%d row%s)\n\n", m, m == 1 ? "" : "s"); - free(fieldMax); - free((void *) fieldNames); - } -} - -/* checks connection validity */ -static int -check_cnx_obj(pgobject * self) -{ - if (!self->valid) - { - PyErr_SetString(IntegrityError, "connection has been closed."); - return 0; - } - return 1; -} - -#ifdef LARGE_OBJECTS -/* checks large object validity */ -static int -check_lo_obj(pglargeobject * self, int level) -{ - if (!check_cnx_obj(self->pgcnx)) - return 0; - - if (!self->lo_oid) - { - PyErr_SetString(IntegrityError, "object is not valid (null oid)."); - return 0; - } - - if (level & CHECK_OPEN) - { - if (self->lo_fd < 0) - { - PyErr_SetString(PyExc_IOError, "object is not opened."); - return 0; - } - } - - if (level & CHECK_CLOSE) - { - if (self->lo_fd >= 0) - { - PyErr_SetString(PyExc_IOError, "object is already opened."); - return 0; - } - } - - return 1; -} -#endif /* LARGE_OBJECTS */ - -/* checks source object validity */ -static int -check_source_obj(pgsourceobject * self, int level) -{ - if (!self->valid) - { - PyErr_SetString(IntegrityError, "object has been closed"); - return 0; - } - - if ((level & CHECK_RESULT) && self->last_result == NULL) - { - PyErr_SetString(DatabaseError, "no result."); - return 0; - } - - if ((level & CHECK_DQL) && self->result_type != RESULT_DQL) - { - PyErr_SetString(DatabaseError, "last query did not return tuples."); - return 0; - } - - if ((level & CHECK_CNX) && !check_cnx_obj(self->pgcnx)) - return 0; - - return 1; -} - -/* shared functions for converting PG types to Python types */ -int * -get_type_array(PGresult *result, int nfields) -{ - int *typ; - int j; - - if ((typ = malloc(sizeof(int) * nfields)) == NULL) - { - PyErr_SetString(PyExc_MemoryError, "memory error in getresult()."); - return NULL; - } - - for (j = 0; j < nfields; j++) - { - switch (PQftype(result, j)) - { - case INT2OID: - case INT4OID: - case OIDOID: - typ[j] = 1; - break; - - case INT8OID: - typ[j] = 2; - break; - - case FLOAT4OID: - case FLOAT8OID: - typ[j] = 3; - break; - - case NUMERICOID: - typ[j] = 4; - break; - - case CASHOID: - typ[j] = 5; - break; - - default: - typ[j] = 6; - break; - } - } - - return typ; -} - - -/* prototypes for constructors */ -static pgsourceobject *pgsource_new(pgobject * pgcnx); - -/* --------------------------------------------------------------------- */ -/* PG SOURCE OBJECT IMPLEMENTATION */ - -/* constructor (internal use only) */ -static pgsourceobject * -pgsource_new(pgobject * pgcnx) -{ - pgsourceobject *npgobj; - - /* allocates new query object */ - if ((npgobj = PyObject_NEW(pgsourceobject, &PgSourceType)) == NULL) - return NULL; - - /* initializes internal parameters */ - Py_XINCREF(pgcnx); - npgobj->pgcnx = pgcnx; - npgobj->last_result = NULL; - npgobj->valid = 1; - npgobj->arraysize = PG_ARRAYSIZE; - - return npgobj; -} - -/* destructor */ -static void -pgsource_dealloc(pgsourceobject * self) -{ - if (self->last_result) - PQclear(self->last_result); - - Py_XDECREF(self->pgcnx); - PyObject_Del(self); -} - -/* closes object */ -static char pgsource_close__doc__[] = -"close() -- close query object without deleting it. " -"All instances of the query object can no longer be used after this call."; - -static PyObject * -pgsource_close(pgsourceobject * self, PyObject * args) -{ - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, "method close() takes no parameter."); - return NULL; - } - - /* frees result if necessary and invalidates object */ - if (self->last_result) - { - PQclear(self->last_result); - self->result_type = RESULT_EMPTY; - self->last_result = NULL; - } - - self->valid = 0; - - /* return None */ - Py_INCREF(Py_None); - return Py_None; -} - -/* database query */ -static char pgsource_execute__doc__[] = -"execute(sql) -- execute a SQL statement (string).\n " -"On success, this call returns the number of affected rows, " -"or None for DQL (SELECT, ...) statements.\n" -"The fetch (fetch(), fetchone() and fetchall()) methods can be used " -"to get result rows."; - -static PyObject * -pgsource_execute(pgsourceobject * self, PyObject * args) -{ - char *query; - - /* checks validity */ - if (!check_source_obj(self, CHECK_CNX)) - return NULL; - - /* make sure that the connection object is valid */ - if (!self->pgcnx->cnx) - return NULL; - - /* get query args */ - if (!PyArg_ParseTuple(args, "s", &query)) - { - PyErr_SetString(PyExc_TypeError, "execute(sql), with sql (string)."); - return NULL; - } - - /* frees previous result */ - if (self->last_result) - { - PQclear(self->last_result); - self->last_result = NULL; - } - self->max_row = 0; - self->current_row = 0; - self->num_fields = 0; - - /* gets result */ - Py_BEGIN_ALLOW_THREADS - self->last_result = PQexec(self->pgcnx->cnx, query); - Py_END_ALLOW_THREADS - - /* checks result validity */ - if (!self->last_result) - { - PyErr_SetString(PyExc_ValueError, PQerrorMessage(self->pgcnx->cnx)); - return NULL; - } - - /* checks result status */ - switch (PQresultStatus(self->last_result)) - { - long num_rows; - char *temp; - - /* query succeeded */ - case PGRES_TUPLES_OK: /* DQL: returns None (DB-SIG compliant) */ - self->result_type = RESULT_DQL; - self->max_row = PQntuples(self->last_result); - self->num_fields = PQnfields(self->last_result); - Py_INCREF(Py_None); - return Py_None; - case PGRES_COMMAND_OK: /* other requests */ - case PGRES_COPY_OUT: - case PGRES_COPY_IN: - self->result_type = RESULT_DDL; - temp = PQcmdTuples(self->last_result); - num_rows = -1; - if (temp[0]) - { - self->result_type = RESULT_DML; - num_rows = atol(temp); - } - return PyInt_FromLong(num_rows); - - /* query failed */ - case PGRES_EMPTY_QUERY: - PyErr_SetString(PyExc_ValueError, "empty query."); - break; - case PGRES_BAD_RESPONSE: - case PGRES_FATAL_ERROR: - case PGRES_NONFATAL_ERROR: - PyErr_SetString(ProgrammingError, PQerrorMessage(self->pgcnx->cnx)); - break; - default: - PyErr_SetString(InternalError, "internal error: " - "unknown result status."); - break; - } - - /* frees result and returns error */ - PQclear(self->last_result); - self->last_result = NULL; - self->result_type = RESULT_EMPTY; - return NULL; -} - -/* gets oid status for last query (valid for INSERTs, 0 for other) */ -static char pgsource_oidstatus__doc__[] = -"oidstatus() -- return oid of last inserted row (if available)."; - -static PyObject * -pgsource_oidstatus(pgsourceobject * self, PyObject * args) -{ - Oid oid; - - /* checks validity */ - if (!check_source_obj(self, CHECK_RESULT)) - return NULL; - - /* checks args */ - if ((args != NULL) && (!PyArg_ParseTuple(args, ""))) - { - PyErr_SetString(PyExc_TypeError, - "method oidstatus() takes no parameters."); - return NULL; - } - - /* retrieves oid status */ - if ((oid = PQoidValue(self->last_result)) == InvalidOid) - { - Py_INCREF(Py_None); - return Py_None; - } - - return PyInt_FromLong(oid); -} - -/* fetches rows from last result */ -static char pgsource_fetch__doc__[] = -"fetch(num) -- return the next num rows from the last result in a list. " -"If num parameter is omitted arraysize attribute value is used. " -"If size equals -1, all rows are fetched."; - -static PyObject * -pgsource_fetch(pgsourceobject * self, PyObject * args) -{ - PyObject *rowtuple, - *reslist, - *str; - int i, - j; - long size; - - /* checks validity */ - if (!check_source_obj(self, CHECK_RESULT | CHECK_DQL)) - return NULL; - - /* checks args */ - size = self->arraysize; - if (!PyArg_ParseTuple(args, "|l", &size)) - { - PyErr_SetString(PyExc_TypeError, - "fetch(num), with num (integer, optional)."); - return NULL; - } - - /* seeks last line */ - /* limit size to be within the amount of data we actually have */ - if (size == -1 || (self->max_row - self->current_row) < size) - size = self->max_row - self->current_row; - - /* allocate list for result */ - if ((reslist = PyList_New(0)) == NULL) - return NULL; - - /* builds result */ - for (i = 0; i < size; ++i) - { - if ((rowtuple = PyTuple_New(self->num_fields)) == NULL) - { - Py_DECREF(reslist); - return NULL; - } - - for (j = 0; j < self->num_fields; j++) - { - if (PQgetisnull(self->last_result, self->current_row, j)) - { - Py_INCREF(Py_None); - str = Py_None; - } - else - str = PyString_FromString(PQgetvalue(self->last_result, self->current_row, j)); - - PyTuple_SET_ITEM(rowtuple, j, str); - } - - PyList_Append(reslist, rowtuple); - Py_DECREF(rowtuple); - self->current_row++; - } - - return reslist; -} - -/* changes current row (internal wrapper for all "move" methods) */ -static PyObject * -pgsource_move(pgsourceobject * self, PyObject * args, int move) -{ - /* checks validity */ - if (!check_source_obj(self, CHECK_RESULT | CHECK_DQL)) - return NULL; - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - char errbuf[256]; - PyOS_snprintf(errbuf, sizeof(errbuf), - "method %s() takes no parameter.", __movename[move]); - PyErr_SetString(PyExc_TypeError, errbuf); - return NULL; - } - - /* changes the current row */ - switch (move) - { - case QUERY_MOVEFIRST: - self->current_row = 0; - break; - case QUERY_MOVELAST: - self->current_row = self->max_row - 1; - break; - case QUERY_MOVENEXT: - if (self->current_row != self->max_row) - self->current_row++; - break; - case QUERY_MOVEPREV: - if (self->current_row > 0) - self->current_row--; - break; - } - - Py_INCREF(Py_None); - return Py_None; -} - -/* move to first result row */ -static char pgsource_movefirst__doc__[] = -"movefirst() -- move to first result row."; - -static PyObject * -pgsource_movefirst(pgsourceobject * self, PyObject * args) -{ - return pgsource_move(self, args, QUERY_MOVEFIRST); -} - -/* move to last result row */ -static char pgsource_movelast__doc__[] = -"movelast() -- move to last valid result row."; - -static PyObject * -pgsource_movelast(pgsourceobject * self, PyObject * args) -{ - return pgsource_move(self, args, QUERY_MOVELAST); -} - -/* move to next result row */ -static char pgsource_movenext__doc__[] = -"movenext() -- move to next result row."; - -static PyObject * -pgsource_movenext(pgsourceobject * self, PyObject * args) -{ - return pgsource_move(self, args, QUERY_MOVENEXT); -} - -/* move to previous result row */ -static char pgsource_moveprev__doc__[] = -"moveprev() -- move to previous result row."; - -static PyObject * -pgsource_moveprev(pgsourceobject * self, PyObject * args) -{ - return pgsource_move(self, args, QUERY_MOVEPREV); -} - -/* finds field number from string/integer (internal use only) */ -static int -pgsource_fieldindex(pgsourceobject * self, PyObject * param, const char *usage) -{ - int num; - - /* checks validity */ - if (!check_source_obj(self, CHECK_RESULT | CHECK_DQL)) - return -1; - - /* gets field number */ - if (PyString_Check(param)) - num = PQfnumber(self->last_result, PyString_AsString(param)); - else if (PyInt_Check(param)) - num = PyInt_AsLong(param); - else - { - PyErr_SetString(PyExc_TypeError, usage); - return -1; - } - - /* checks field validity */ - if (num < 0 || num >= self->num_fields) - { - PyErr_SetString(PyExc_ValueError, "Unknown field."); - return -1; - } - - return num; -} - -/* builds field information from position (internal use only) */ -static PyObject * -pgsource_buildinfo(pgsourceobject * self, int num) -{ - PyObject *result; - - /* allocates tuple */ - result = PyTuple_New(3); - if (!result) - return NULL; - - /* affects field information */ - PyTuple_SET_ITEM(result, 0, PyInt_FromLong(num)); - PyTuple_SET_ITEM(result, 1, - PyString_FromString(PQfname(self->last_result, num))); - PyTuple_SET_ITEM(result, 2, - PyInt_FromLong(PQftype(self->last_result, num))); - - return result; -} - -/* lists fields info */ -static char pgsource_listinfo__doc__[] = -"listinfo() -- return information for all fields " -"(position, name, type oid)."; - -static PyObject * -pgsource_listinfo(pgsourceobject * self, PyObject * args) -{ - int i; - PyObject *result, - *info; - - /* checks validity */ - if (!check_source_obj(self, CHECK_RESULT | CHECK_DQL)) - return NULL; - - /* gets args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method listinfo() takes no parameter."); - return NULL; - } - - /* builds result */ - if ((result = PyTuple_New(self->num_fields)) == NULL) - return NULL; - - for (i = 0; i < self->num_fields; i++) - { - info = pgsource_buildinfo(self, i); - if (!info) - { - Py_DECREF(result); - return NULL; - } - PyTuple_SET_ITEM(result, i, info); - } - - /* returns result */ - return result; -}; - -/* list fields information for last result */ -static char pgsource_fieldinfo__doc__[] = -"fieldinfo(string|integer) -- return specified field information " -"(position, name, type oid)."; - -static PyObject * -pgsource_fieldinfo(pgsourceobject * self, PyObject * args) -{ - static const char short_usage[] = - "fieldinfo(desc), with desc (string|integer)."; - int num; - PyObject *param; - - /* gets args */ - if (!PyArg_ParseTuple(args, "O", ¶m)) - { - PyErr_SetString(PyExc_TypeError, short_usage); - return NULL; - } - - /* checks args and validity */ - if ((num = pgsource_fieldindex(self, param, short_usage)) == -1) - return NULL; - - /* returns result */ - return pgsource_buildinfo(self, num); -}; - -/* retrieve field value */ -static char pgsource_field__doc__[] = -"field(string|integer) -- return specified field value."; - -static PyObject * -pgsource_field(pgsourceobject * self, PyObject * args) -{ - static const char short_usage[] = - "field(desc), with desc (string|integer)."; - int num; - PyObject *param; - - /* gets args */ - if (!PyArg_ParseTuple(args, "O", ¶m)) - { - PyErr_SetString(PyExc_TypeError, short_usage); - return NULL; - } - - /* checks args and validity */ - if ((num = pgsource_fieldindex(self, param, short_usage)) == -1) - return NULL; - - return PyString_FromString(PQgetvalue(self->last_result, - self->current_row, num)); -} - -/* query object methods */ -static PyMethodDef pgsource_methods[] = { - {"close", (PyCFunction) pgsource_close, METH_VARARGS, - pgsource_close__doc__}, - {"execute", (PyCFunction) pgsource_execute, METH_VARARGS, - pgsource_execute__doc__}, - {"oidstatus", (PyCFunction) pgsource_oidstatus, METH_VARARGS, - pgsource_oidstatus__doc__}, - {"fetch", (PyCFunction) pgsource_fetch, METH_VARARGS, - pgsource_fetch__doc__}, - {"movefirst", (PyCFunction) pgsource_movefirst, METH_VARARGS, - pgsource_movefirst__doc__}, - {"movelast", (PyCFunction) pgsource_movelast, METH_VARARGS, - pgsource_movelast__doc__}, - {"movenext", (PyCFunction) pgsource_movenext, METH_VARARGS, - pgsource_movenext__doc__}, - {"moveprev", (PyCFunction) pgsource_moveprev, METH_VARARGS, - pgsource_moveprev__doc__}, - {"field", (PyCFunction) pgsource_field, METH_VARARGS, - pgsource_field__doc__}, - {"fieldinfo", (PyCFunction) pgsource_fieldinfo, METH_VARARGS, - pgsource_fieldinfo__doc__}, - {"listinfo", (PyCFunction) pgsource_listinfo, METH_VARARGS, - pgsource_listinfo__doc__}, - {NULL, NULL} -}; - -/* gets query object attributes */ -static PyObject * -pgsource_getattr(pgsourceobject * self, char *name) -{ - /* pg connection object */ - if (!strcmp(name, "pgcnx")) - { - if (check_source_obj(self, 0)) - { - Py_INCREF(self->pgcnx); - return (PyObject *) (self->pgcnx); - } - Py_INCREF(Py_None); - return Py_None; - } - - /* arraysize */ - if (!strcmp(name, "arraysize")) - return PyInt_FromLong(self->arraysize); - - /* resulttype */ - if (!strcmp(name, "resulttype")) - return PyInt_FromLong(self->result_type); - - /* ntuples */ - if (!strcmp(name, "ntuples")) - return PyInt_FromLong(self->max_row); - - /* nfields */ - if (!strcmp(name, "nfields")) - return PyInt_FromLong(self->num_fields); - - /* attributes list */ - if (!strcmp(name, "__members__")) - { - PyObject *list = PyList_New(5); - - PyList_SET_ITEM(list, 0, PyString_FromString("pgcnx")); - PyList_SET_ITEM(list, 1, PyString_FromString("arraysize")); - PyList_SET_ITEM(list, 2, PyString_FromString("resulttype")); - PyList_SET_ITEM(list, 3, PyString_FromString("ntuples")); - PyList_SET_ITEM(list, 4, PyString_FromString("nfields")); - - return list; - } - - /* module name */ - if (!strcmp(name, "__module__")) - return PyString_FromString(MODULE_NAME); - - /* class name */ - if (!strcmp(name, "__class__")) - return PyString_FromString("pgsource"); - - /* seeks name in methods (fallback) */ - return Py_FindMethod(pgsource_methods, (PyObject *) self, name); -} - -/* sets query object attributes */ -static int -pgsource_setattr(pgsourceobject * self, char *name, PyObject * v) -{ - /* arraysize */ - if (!strcmp(name, "arraysize")) - { - if (!PyInt_Check(v)) - { - PyErr_SetString(PyExc_TypeError, "arraysize must be integer."); - return -1; - } - - self->arraysize = PyInt_AsLong(v); - return 0; - } - - /* unknown attribute */ - PyErr_SetString(PyExc_TypeError, "not a writable attribute."); - return -1; -} - -/* prints query object in human readable format */ - -static int -pgsource_print(pgsourceobject * self, FILE *fp, int flags) -{ - switch (self->result_type) - { - case RESULT_DQL: - print_result(fp, self->last_result); - break; - case RESULT_DDL: - case RESULT_DML: - fputs(PQcmdStatus(self->last_result), fp); - break; - case RESULT_EMPTY: - default: - fputs("Empty PostgreSQL source object.", fp); - break; - } - - return 0; -} - -/* query type definition */ -staticforward PyTypeObject PgSourceType = { - PyObject_HEAD_INIT(NULL) - - 0, /* ob_size */ - "pgsourceobject", /* tp_name */ - sizeof(pgsourceobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor) pgsource_dealloc, /* tp_dealloc */ - (printfunc) pgsource_print, /* tp_print */ - (getattrfunc) pgsource_getattr, /* tp_getattr */ - (setattrfunc) pgsource_setattr, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ -}; - -/* --------------------------------------------------------------------- */ -/* PG "LARGE" OBJECT IMPLEMENTATION */ - -#ifdef LARGE_OBJECTS - -/* constructor (internal use only) */ -static pglargeobject * -pglarge_new(pgobject * pgcnx, Oid oid) -{ - pglargeobject *npglo; - - if ((npglo = PyObject_NEW(pglargeobject, &PglargeType)) == NULL) - return NULL; - - Py_XINCREF(pgcnx); - npglo->pgcnx = pgcnx; - npglo->lo_fd = -1; - npglo->lo_oid = oid; - - return npglo; -} - -/* destructor */ -static void -pglarge_dealloc(pglargeobject * self) -{ - if (self->lo_fd >= 0 && check_cnx_obj(self->pgcnx)) - lo_close(self->pgcnx->cnx, self->lo_fd); - - Py_XDECREF(self->pgcnx); - PyObject_Del(self); -} - -/* opens large object */ -static char pglarge_open__doc__[] = -"open(mode) -- open access to large object with specified mode " -"(INV_READ, INV_WRITE constants defined by module)."; - -static PyObject * -pglarge_open(pglargeobject * self, PyObject * args) -{ - int mode, - fd; - - /* check validity */ - if (!check_lo_obj(self, CHECK_CLOSE)) - return NULL; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "i", &mode)) - { - PyErr_SetString(PyExc_TypeError, "open(mode), with mode(integer)."); - return NULL; - } - - /* opens large object */ - if ((fd = lo_open(self->pgcnx->cnx, self->lo_oid, mode)) < 0) - { - PyErr_SetString(PyExc_IOError, "can't open large object."); - return NULL; - } - self->lo_fd = fd; - - /* no error : returns Py_None */ - Py_INCREF(Py_None); - return Py_None; -} - -/* close large object */ -static char pglarge_close__doc__[] = -"close() -- close access to large object data."; - -static PyObject * -pglarge_close(pglargeobject * self, PyObject * args) -{ - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method close() takes no parameters."); - return NULL; - } - - /* checks validity */ - if (!check_lo_obj(self, CHECK_OPEN)) - return NULL; - - /* closes large object */ - if (lo_close(self->pgcnx->cnx, self->lo_fd)) - { - PyErr_SetString(PyExc_IOError, "error while closing large object fd."); - return NULL; - } - self->lo_fd = -1; - - /* no error : returns Py_None */ - Py_INCREF(Py_None); - return Py_None; -} - -/* reads from large object */ -static char pglarge_read__doc__[] = -"read(integer) -- read from large object to sized string. " -"Object must be opened in read mode before calling this method."; - -static PyObject * -pglarge_read(pglargeobject * self, PyObject * args) -{ - int size; - PyObject *buffer; - - /* checks validity */ - if (!check_lo_obj(self, CHECK_OPEN)) - return NULL; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "i", &size)) - { - PyErr_SetString(PyExc_TypeError, "read(size), with size (integer)."); - return NULL; - } - - if (size <= 0) - { - PyErr_SetString(PyExc_ValueError, "size must be positive."); - return NULL; - } - - /* allocate buffer and runs read */ - buffer = PyString_FromStringAndSize((char *) NULL, size); - - if ((size = lo_read(self->pgcnx->cnx, self->lo_fd, BUF(buffer), size)) < 0) - { - PyErr_SetString(PyExc_IOError, "error while reading."); - Py_XDECREF(buffer); - return NULL; - } - - /* resize buffer and returns it */ - _PyString_Resize(&buffer, size); - return buffer; -} - -/* write to large object */ -static char pglarge_write__doc__[] = -"write(string) -- write sized string to large object. " -"Object must be opened in read mode before calling this method."; - -static PyObject * -pglarge_write(pglargeobject * self, PyObject * args) -{ - char *buffer; - int size, - bufsize; - - /* checks validity */ - if (!check_lo_obj(self, CHECK_OPEN)) - return NULL; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize)) - { - PyErr_SetString(PyExc_TypeError, - "write(buffer), with buffer (sized string)."); - return NULL; - } - - /* sends query */ - if ((size = lo_write(self->pgcnx->cnx, self->lo_fd, buffer, - bufsize)) < bufsize) - { - PyErr_SetString(PyExc_IOError, "buffer truncated during write."); - return NULL; - } - - /* no error : returns Py_None */ - Py_INCREF(Py_None); - return Py_None; -} - -/* go to position in large object */ -static char pglarge_seek__doc__[] = -"seek(off, whence) -- move to specified position. Object must be opened " -"before calling this method. whence can be SEEK_SET, SEEK_CUR or SEEK_END, " -"constants defined by module."; - -static PyObject * -pglarge_lseek(pglargeobject * self, PyObject * args) -{ - /* offset and whence are initialized to keep compiler happy */ - int ret, - offset = 0, - whence = 0; - - /* checks validity */ - if (!check_lo_obj(self, CHECK_OPEN)) - return NULL; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "ii", &offset, &whence)) - { - PyErr_SetString(PyExc_TypeError, - "lseek(offset, whence), with offset and whence (integers)."); - return NULL; - } - - /* sends query */ - if ((ret = lo_lseek(self->pgcnx->cnx, self->lo_fd, offset, whence)) == -1) - { - PyErr_SetString(PyExc_IOError, "error while moving cursor."); - return NULL; - } - - /* returns position */ - return PyInt_FromLong(ret); -} - -/* gets large object size */ -static char pglarge_size__doc__[] = -"size() -- return large object size. " -"Object must be opened before calling this method."; - -static PyObject * -pglarge_size(pglargeobject * self, PyObject * args) -{ - int start, - end; - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method size() takes no parameters."); - return NULL; - } - - /* checks validity */ - if (!check_lo_obj(self, CHECK_OPEN)) - return NULL; - - /* gets current position */ - if ((start = lo_tell(self->pgcnx->cnx, self->lo_fd)) == -1) - { - PyErr_SetString(PyExc_IOError, "error while getting current position."); - return NULL; - } - - /* gets end position */ - if ((end = lo_lseek(self->pgcnx->cnx, self->lo_fd, 0, SEEK_END)) == -1) - { - PyErr_SetString(PyExc_IOError, "error while getting end position."); - return NULL; - } - - /* move back to start position */ - if ((start = lo_lseek(self->pgcnx->cnx, self->lo_fd, start, SEEK_SET)) == -1) - { - PyErr_SetString(PyExc_IOError, - "error while moving back to first position."); - return NULL; - } - - /* returns size */ - return PyInt_FromLong(end); -} - -/* gets large object cursor position */ -static char pglarge_tell__doc__[] = -"tell() -- give current position in large object. " -"Object must be opened before calling this method."; - -static PyObject * -pglarge_tell(pglargeobject * self, PyObject * args) -{ - int start; - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method tell() takes no parameters."); - return NULL; - } - - /* checks validity */ - if (!check_lo_obj(self, CHECK_OPEN)) - return NULL; - - /* gets current position */ - if ((start = lo_tell(self->pgcnx->cnx, self->lo_fd)) == -1) - { - PyErr_SetString(PyExc_IOError, "error while getting position."); - return NULL; - } - - /* returns size */ - return PyInt_FromLong(start); -} - -/* exports large object as unix file */ -static char pglarge_export__doc__[] = -"export(string) -- export large object data to specified file. " -"Object must be closed when calling this method."; - -static PyObject * -pglarge_export(pglargeobject * self, PyObject * args) -{ - char *name; - - /* checks validity */ - if (!check_lo_obj(self, CHECK_CLOSE)) - return NULL; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "s", &name)) - { - PyErr_SetString(PyExc_TypeError, - "export(filename), with filename (string)."); - return NULL; - } - - /* runs command */ - if (!lo_export(self->pgcnx->cnx, self->lo_oid, name)) - { - PyErr_SetString(PyExc_IOError, "error while exporting large object."); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -/* deletes a large object */ -static char pglarge_unlink__doc__[] = -"unlink() -- destroy large object. " -"Object must be closed when calling this method."; - -static PyObject * -pglarge_unlink(pglargeobject * self, PyObject * args) -{ - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method unlink() takes no parameters."); - return NULL; - } - - /* checks validity */ - if (!check_lo_obj(self, CHECK_CLOSE)) - return NULL; - - /* deletes the object, invalidate it on success */ - if (!lo_unlink(self->pgcnx->cnx, self->lo_oid)) - { - PyErr_SetString(PyExc_IOError, "error while unlinking large object"); - return NULL; - } - self->lo_oid = 0; - - Py_INCREF(Py_None); - return Py_None; -} - -/* large object methods */ -static struct PyMethodDef pglarge_methods[] = { - {"open", (PyCFunction) pglarge_open, METH_VARARGS, pglarge_open__doc__}, - {"close", (PyCFunction) pglarge_close, METH_VARARGS, pglarge_close__doc__}, - {"read", (PyCFunction) pglarge_read, METH_VARARGS, pglarge_read__doc__}, - {"write", (PyCFunction) pglarge_write, METH_VARARGS, pglarge_write__doc__}, - {"seek", (PyCFunction) pglarge_lseek, METH_VARARGS, pglarge_seek__doc__}, - {"size", (PyCFunction) pglarge_size, METH_VARARGS, pglarge_size__doc__}, - {"tell", (PyCFunction) pglarge_tell, METH_VARARGS, pglarge_tell__doc__}, - {"export",(PyCFunction) pglarge_export,METH_VARARGS,pglarge_export__doc__}, - {"unlink",(PyCFunction) pglarge_unlink,METH_VARARGS,pglarge_unlink__doc__}, - {NULL, NULL} -}; - -/* get attribute */ -static PyObject * -pglarge_getattr(pglargeobject * self, char *name) -{ - /* list postgreSQL large object fields */ - - /* associated pg connection object */ - if (!strcmp(name, "pgcnx")) - { - if (check_lo_obj(self, 0)) - { - Py_INCREF(self->pgcnx); - return (PyObject *) (self->pgcnx); - } - - Py_INCREF(Py_None); - return Py_None; - } - - /* large object oid */ - if (!strcmp(name, "oid")) - { - if (check_lo_obj(self, 0)) - return PyInt_FromLong(self->lo_oid); - - Py_INCREF(Py_None); - return Py_None; - } - - /* error (status) message */ - if (!strcmp(name, "error")) - return PyString_FromString(PQerrorMessage(self->pgcnx->cnx)); - - /* attributes list */ - if (!strcmp(name, "__members__")) - { - PyObject *list = PyList_New(3); - - if (list) - { - PyList_SET_ITEM(list, 0, PyString_FromString("oid")); - PyList_SET_ITEM(list, 1, PyString_FromString("pgcnx")); - PyList_SET_ITEM(list, 2, PyString_FromString("error")); - } - - return list; - } - - /* module name */ - if (!strcmp(name, "__module__")) - return PyString_FromString(MODULE_NAME); - - /* class name */ - if (!strcmp(name, "__class__")) - return PyString_FromString("pglarge"); - - /* seeks name in methods (fallback) */ - return Py_FindMethod(pglarge_methods, (PyObject *) self, name); -} - -/* prints query object in human readable format */ -static int -pglarge_print(pglargeobject * self, FILE *fp, int flags) -{ - char print_buffer[128]; - PyOS_snprintf(print_buffer, sizeof(print_buffer), - self->lo_fd >= 0 ? - "Opened large object, oid %ld" : - "Closed large object, oid %ld", (long) self->lo_oid); - fputs(print_buffer, fp); - return 0; -} - -/* object type definition */ -staticforward PyTypeObject PglargeType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "pglarge", /* tp_name */ - sizeof(pglargeobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - - /* methods */ - (destructor) pglarge_dealloc, /* tp_dealloc */ - (printfunc) pglarge_print, /* tp_print */ - (getattrfunc) pglarge_getattr, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ -}; -#endif /* LARGE_OBJECTS */ - - -/* --------------------------------------------------------------------- */ -/* PG QUERY OBJECT IMPLEMENTATION */ - -/* connects to a database */ -static char connect__doc__[] = -"connect(dbname, host, port, opt, tty) -- connect to a PostgreSQL database " -"using specified parameters (optionals, keywords aware)."; - -static PyObject * -pgconnect(pgobject * self, PyObject * args, PyObject * dict) -{ - static const char *kwlist[] = {"dbname", "host", "port", "opt", - "tty", "user", "passwd", NULL}; - - char *pghost, - *pgopt, - *pgtty, - *pgdbname, - *pguser, - *pgpasswd; - int pgport; - char port_buffer[20]; - pgobject *npgobj; - - pghost = pgopt = pgtty = pgdbname = pguser = pgpasswd = NULL; - pgport = -1; - - /* - * parses standard arguments With the right compiler warnings, this - * will issue a diagnostic. There is really no way around it. If I - * don't declare kwlist as const char *kwlist[] then it complains when - * I try to assign all those constant strings to it. - */ - if (!PyArg_ParseTupleAndKeywords(args, dict, "|zzizzzz", (char **) kwlist, - &pgdbname, &pghost, &pgport, &pgopt, &pgtty, &pguser, &pgpasswd)) - return NULL; - -#ifdef DEFAULT_VARS - /* handles defaults variables (for uninitialised vars) */ - if ((!pghost) && (pg_default_host != Py_None)) - pghost = PyString_AsString(pg_default_host); - - if ((pgport == -1) && (pg_default_port != Py_None)) - pgport = PyInt_AsLong(pg_default_port); - - if ((!pgopt) && (pg_default_opt != Py_None)) - pgopt = PyString_AsString(pg_default_opt); - - if ((!pgtty) && (pg_default_tty != Py_None)) - pgtty = PyString_AsString(pg_default_tty); - - if ((!pgdbname) && (pg_default_base != Py_None)) - pgdbname = PyString_AsString(pg_default_base); - - if ((!pguser) && (pg_default_user != Py_None)) - pguser = PyString_AsString(pg_default_user); - - if ((!pgpasswd) && (pg_default_passwd != Py_None)) - pgpasswd = PyString_AsString(pg_default_passwd); -#endif /* DEFAULT_VARS */ - - if ((npgobj = (pgobject *) pgobject_New()) == NULL) - return NULL; - - if (pgport != -1) - { - memset(port_buffer, 0, sizeof(port_buffer)); - sprintf(port_buffer, "%d", pgport); - } - -#ifdef PQsetdbLoginIsThreadSafe - Py_BEGIN_ALLOW_THREADS -#endif - npgobj->cnx = PQsetdbLogin(pghost, pgport == -1 ? NULL : port_buffer, - pgopt, pgtty, pgdbname, pguser, pgpasswd); -#ifdef PQsetdbLoginIsThreadSafe - Py_END_ALLOW_THREADS -#endif - - if (PQstatus(npgobj->cnx) == CONNECTION_BAD) - { - PyErr_SetString(InternalError, PQerrorMessage(npgobj->cnx)); - Py_XDECREF(npgobj); - return NULL; - } - -#ifdef HANDLE_NOTICES - PQsetNoticeProcessor(npgobj->cnx, notice_processor, npgobj); -#endif /* HANDLE_NOTICES */ - - return (PyObject *) npgobj; -} - -/* pgobject methods */ - -/* destructor */ -static void -pg_dealloc(pgobject * self) -{ - if (self->cnx) - { - Py_BEGIN_ALLOW_THREADS - PQfinish(self->cnx); - Py_END_ALLOW_THREADS - } - PyObject_Del(self); -} - -/* close without deleting */ -static char pg_close__doc__[] = -"close() -- close connection. All instances of the connection object and " -"derived objects (queries and large objects) can no longer be used after " -"this call."; - -static PyObject * -pg_close(pgobject * self, PyObject * args) -{ - /* gets args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, "close()."); - return NULL; - } - - /* connection object cannot already be closed */ - if (!self->cnx) - { - PyErr_SetString(InternalError, "Connection already closed"); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - PQfinish(self->cnx); - Py_END_ALLOW_THREADS - - self->cnx = NULL; - Py_INCREF(Py_None); - return Py_None; -} - -static void -pgquery_dealloc(pgqueryobject * self) -{ - if (self->last_result) - PQclear(self->last_result); - - PyObject_Del(self); -} - -/* resets connection */ -static char pg_reset__doc__[] = -"reset() -- reset connection with current parameters. All derived queries " -"and large objects derived from this connection will not be usable after " -"this call."; - -static PyObject * -pg_reset(pgobject * self, PyObject * args) -{ - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method reset() takes no parameters."); - return NULL; - } - - /* resets the connection */ - PQreset(self->cnx); - Py_INCREF(Py_None); - return Py_None; -} - -/* cancels current command */ -static char pg_cancel__doc__[] = -"cancel() -- abandon processing of the current command."; - -static PyObject * -pg_cancel(pgobject * self, PyObject * args) -{ - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method cancel() takes no parameters."); - return NULL; - } - - /* request that the server abandon processing of the current command */ - return PyInt_FromLong((long) PQrequestCancel(self->cnx)); -} - -/* get connection socket */ -static char pg_fileno__doc__[] = -"fileno() -- return database connection socket file handle."; - -static PyObject * -pg_fileno(pgobject * self, PyObject * args) -{ - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method fileno() takes no parameters."); - return NULL; - } - -#ifdef NO_PQSOCKET - return PyInt_FromLong((long) self->cnx->sock); -#else - return PyInt_FromLong((long) PQsocket(self->cnx)); -#endif -} - -/* get number of rows */ -static char pgquery_ntuples__doc__[] = -"ntuples() -- returns number of tuples returned by query."; - -static PyObject * -pgquery_ntuples(pgqueryobject * self, PyObject * args) -{ - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method ntuples() takes no parameters."); - return NULL; - } - - return PyInt_FromLong((long) PQntuples(self->last_result)); -} - -/* list fields names from query result */ -static char pgquery_listfields__doc__[] = -"listfields() -- Lists field names from result."; - -static PyObject * -pgquery_listfields(pgqueryobject * self, PyObject * args) -{ - int i, - n; - char *name; - PyObject *fieldstuple, - *str; - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method listfields() takes no parameters."); - return NULL; - } - - /* builds tuple */ - n = PQnfields(self->last_result); - fieldstuple = PyTuple_New(n); - - if (fieldstuple == NULL) - return NULL; - - for (i = 0; i < n; i++) - { - name = PQfname(self->last_result, i); - str = PyString_FromString(name); - PyTuple_SET_ITEM(fieldstuple, i, str); - } - - return fieldstuple; -} - -/* get field name from last result */ -static char pgquery_fieldname__doc__[] = -"fieldname() -- returns name of field from result from its position."; - -static PyObject * -pgquery_fieldname(pgqueryobject * self, PyObject * args) -{ - int i; - char *name; - - /* gets args */ - if (!PyArg_ParseTuple(args, "i", &i)) - { - PyErr_SetString(PyExc_TypeError, - "fieldname(number), with number(integer)."); - return NULL; - } - - /* checks number validity */ - if (i >= PQnfields(self->last_result)) - { - PyErr_SetString(PyExc_ValueError, "invalid field number."); - return NULL; - } - - /* gets fields name and builds object */ - name = PQfname(self->last_result, i); - return PyString_FromString(name); -} - -/* gets fields number from name in last result */ -static char pgquery_fieldnum__doc__[] = -"fieldnum() -- returns position in query for field from its name."; - -static PyObject * -pgquery_fieldnum(pgqueryobject * self, PyObject * args) -{ - int num; - char *name; - - /* gets args */ - if (!PyArg_ParseTuple(args, "s", &name)) - { - PyErr_SetString(PyExc_TypeError, "fieldnum(name), with name (string)."); - return NULL; - } - - /* gets field number */ - if ((num = PQfnumber(self->last_result, name)) == -1) - { - PyErr_SetString(PyExc_ValueError, "Unknown field."); - return NULL; - } - - return PyInt_FromLong(num); -} - -/* retrieves last result */ -static char pgquery_getresult__doc__[] = -"getresult() -- Gets the result of a query. The result is returned " -"as a list of rows, each one a list of fields in the order returned " -"by the server."; - -static PyObject * -pgquery_getresult(pgqueryobject * self, PyObject * args) -{ - PyObject *rowtuple, - *reslist, - *val; - int i, - j, - m, - n, - *typ; - - /* checks args (args == NULL for an internal call) */ - if ((args != NULL) && (!PyArg_ParseTuple(args, ""))) - { - PyErr_SetString(PyExc_TypeError, - "method getresult() takes no parameters."); - return NULL; - } - - /* stores result in tuple */ - m = PQntuples(self->last_result); - n = PQnfields(self->last_result); - reslist = PyList_New(m); - - typ = get_type_array(self->last_result, n); - - for (i = 0; i < m; i++) - { - if ((rowtuple = PyTuple_New(n)) == NULL) - { - Py_DECREF(reslist); - reslist = NULL; - goto exit; - } - - for (j = 0; j < n; j++) - { - int k; - char *s = PQgetvalue(self->last_result, i, j); - char cashbuf[64]; - PyObject *tmp_obj; - - if (PQgetisnull(self->last_result, i, j)) - { - Py_INCREF(Py_None); - val = Py_None; - } - else - switch (typ[j]) - { - case 1: - val = PyInt_FromString(s, NULL, 10); - break; - - case 2: - val = PyLong_FromString(s, NULL, 10); - break; - - case 3: - tmp_obj = PyString_FromString(s); - val = PyFloat_FromString(tmp_obj, NULL); - Py_DECREF(tmp_obj); - break; - - case 5: - for (k = 0; - *s && k < sizeof(cashbuf) / sizeof(cashbuf[0]) - 1; - s++) - { - if (isdigit(*s) || *s == '.') - cashbuf[k++] = *s; - else if (*s == '(' || *s == '-') - cashbuf[k++] = '-'; - } - cashbuf[k] = 0; - s = cashbuf; - - case 4: - if (decimal) - { - tmp_obj = Py_BuildValue("(s)", s); - val = PyEval_CallObject(decimal, tmp_obj); - } - else - { - tmp_obj = PyString_FromString(s); - val = PyFloat_FromString(tmp_obj, NULL); - } - Py_DECREF(tmp_obj); - break; - - default: - val = PyString_FromString(s); - break; - } - - if (val == NULL) - { - Py_DECREF(reslist); - Py_DECREF(rowtuple); - reslist = NULL; - goto exit; - } - - PyTuple_SET_ITEM(rowtuple, j, val); - } - - PyList_SET_ITEM(reslist, i, rowtuple); - } - -exit: - free(typ); - - /* returns list */ - return reslist; -} - -/* retrieves last result as a list of dictionaries*/ -static char pgquery_dictresult__doc__[] = -"dictresult() -- Gets the result of a query. The result is returned " -"as a list of rows, each one a dictionary with the field names used " -"as the labels."; - -static PyObject * -pgquery_dictresult(pgqueryobject * self, PyObject * args) -{ - PyObject *dict, - *reslist, - *val; - int i, - j, - m, - n, - *typ; - - /* checks args (args == NULL for an internal call) */ - if ((args != NULL) && (!PyArg_ParseTuple(args, ""))) - { - PyErr_SetString(PyExc_TypeError, - "method getresult() takes no parameters."); - return NULL; - } - - /* stores result in list */ - m = PQntuples(self->last_result); - n = PQnfields(self->last_result); - reslist = PyList_New(m); - - typ = get_type_array(self->last_result, n); - - for (i = 0; i < m; i++) - { - if ((dict = PyDict_New()) == NULL) - { - Py_DECREF(reslist); - reslist = NULL; - goto exit; - } - - for (j = 0; j < n; j++) - { - int k; - char *s = PQgetvalue(self->last_result, i, j); - char cashbuf[64]; - PyObject *tmp_obj; - - if (PQgetisnull(self->last_result, i, j)) - { - Py_INCREF(Py_None); - val = Py_None; - } - else - switch (typ[j]) - { - case 1: - val = PyInt_FromString(s, NULL, 10); - break; - - case 2: - val = PyLong_FromString(s, NULL, 10); - break; - - case 3: - tmp_obj = PyString_FromString(s); - val = PyFloat_FromString(tmp_obj, NULL); - Py_DECREF(tmp_obj); - break; - - case 5: - for (k = 0; - *s && k < sizeof(cashbuf) / sizeof(cashbuf[0]) - 1; - s++) - { - if (isdigit(*s) || *s == '.') - cashbuf[k++] = *s; - else if (*s == '(' || *s == '-') - cashbuf[k++] = '-'; - } - cashbuf[k] = 0; - s = cashbuf; - - case 4: - if (decimal) - { - tmp_obj = Py_BuildValue("(s)", s); - val = PyEval_CallObject(decimal, tmp_obj); - } - else - { - tmp_obj = PyString_FromString(s); - val = PyFloat_FromString(tmp_obj, NULL); - } - Py_DECREF(tmp_obj); - break; - - default: - val = PyString_FromString(s); - break; - } - - if (val == NULL) - { - Py_DECREF(dict); - Py_DECREF(reslist); - reslist = NULL; - goto exit; - } - - PyDict_SetItemString(dict, PQfname(self->last_result, j), val); - Py_DECREF(val); - } - - PyList_SET_ITEM(reslist, i, dict); - } - -exit: - free(typ); - - /* returns list */ - return reslist; -} - -/* gets asynchronous notify */ -static char pg_getnotify__doc__[] = -"getnotify() -- get database notify for this connection."; - -static PyObject * -pg_getnotify(pgobject * self, PyObject * args) -{ - PGnotify *notify; - - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method getnotify() takes no parameters."); - return NULL; - } - - /* checks for NOTIFY messages */ - PQconsumeInput(self->cnx); - - if ((notify = PQnotifies(self->cnx)) == NULL) - { - Py_INCREF(Py_None); - return Py_None; - } - else - { - PyObject *notify_result, - *temp; - - if ((notify_result = PyTuple_New(2)) == NULL || - (temp = PyString_FromString(notify->relname)) == NULL) - { - return NULL; - } - - PyTuple_SET_ITEM(notify_result, 0, temp); - - if ((temp = PyInt_FromLong(notify->be_pid)) == NULL) - { - Py_DECREF(notify_result); - return NULL; - } - - PyTuple_SET_ITEM(notify_result, 1, temp); - PQfreemem(notify); - return notify_result; - } -} - -/* source creation */ -static char pg_source__doc__[] = -"source() -- creates a new source object for this connection"; - -static PyObject * -pg_source(pgobject * self, PyObject * args) -{ - /* checks validity */ - if (!check_cnx_obj(self)) - return NULL; - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, "method source() takes no parameter."); - return NULL; - } - - /* allocate new pg query object */ - return (PyObject *) pgsource_new(self); -} - -/* database query */ -static char pg_query__doc__[] = -"query(sql) -- creates a new query object for this connection," -" using sql (string) request."; - -static PyObject * -pg_query(pgobject * self, PyObject * args) -{ - char *query; - PGresult *result; - pgqueryobject *npgobj; - int status; - - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* get query args */ - if (!PyArg_ParseTuple(args, "s", &query)) - { - PyErr_SetString(PyExc_TypeError, "query(sql), with sql (string)."); - return NULL; - } - - /* frees previous result */ - if (self->last_result) - { - PQclear(self->last_result); - self->last_result = NULL; - } - - /* gets result */ - Py_BEGIN_ALLOW_THREADS - result = PQexec(self->cnx, query); - Py_END_ALLOW_THREADS - - /* checks result validity */ - if (!result) - { - PyErr_SetString(PyExc_ValueError, PQerrorMessage(self->cnx)); - return NULL; - } - - /* checks result status */ - if ((status = PQresultStatus(result)) != PGRES_TUPLES_OK) - { - switch (status) - { - case PGRES_EMPTY_QUERY: - PyErr_SetString(PyExc_ValueError, "empty query."); - break; - case PGRES_BAD_RESPONSE: - case PGRES_FATAL_ERROR: - case PGRES_NONFATAL_ERROR: - PyErr_SetString(ProgrammingError, PQerrorMessage(self->cnx)); - break; - case PGRES_COMMAND_OK: - { /* INSERT, UPDATE, DELETE */ - Oid oid = PQoidValue(result); - if (oid == InvalidOid) /* not a single insert */ - { - char *ret = PQcmdTuples(result); - - if (ret[0]) /* return number of rows affected */ - { - PyObject* obj = PyString_FromString(ret); - PQclear(result); - return obj; - } - PQclear(result); - Py_INCREF(Py_None); - return Py_None; - } - /* for a single insert, return the oid */ - PQclear(result); - return PyInt_FromLong(oid); - } - case PGRES_COPY_OUT: /* no data will be received */ - case PGRES_COPY_IN: - PQclear(result); - Py_INCREF(Py_None); - return Py_None; - default: - PyErr_SetString(InternalError, "internal error: " - "unknown result status."); - break; - } - - PQclear(result); - return NULL; /* error detected on query */ - } - - if ((npgobj = PyObject_NEW(pgqueryobject, &PgQueryType)) == NULL) - return NULL; - - /* stores result and returns object */ - npgobj->last_result = result; - return (PyObject *) npgobj; -} - -#ifdef DIRECT_ACCESS -static char pg_putline__doc__[] = -"putline() -- sends a line directly to the backend"; - -/* direct acces function : putline */ -static PyObject * -pg_putline(pgobject * self, PyObject * args) -{ - char *line; - - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* reads args */ - if (!PyArg_ParseTuple(args, "s", &line)) - { - PyErr_SetString(PyExc_TypeError, "putline(line), with line (string)."); - return NULL; - } - - /* sends line to backend */ - if (PQputline(self->cnx, line)) - { - PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx)); - return NULL; - } - Py_INCREF(Py_None); - return Py_None; -} - -/* direct access function : getline */ -static char pg_getline__doc__[] = -"getline() -- gets a line directly from the backend."; - -static PyObject * -pg_getline(pgobject * self, PyObject * args) -{ - char line[MAX_BUFFER_SIZE]; - PyObject *str = NULL; /* GCC */ - - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method getline() takes no parameters."); - return NULL; - } - - /* gets line */ - switch (PQgetline(self->cnx, line, MAX_BUFFER_SIZE)) - { - case 0: - str = PyString_FromString(line); - break; - case 1: - PyErr_SetString(PyExc_MemoryError, "buffer overflow"); - str = NULL; - break; - case EOF: - Py_INCREF(Py_None); - str = Py_None; - break; - } - - return str; -} - -/* direct access function : end copy */ -static char pg_endcopy__doc__[] = -"endcopy() -- synchronizes client and server"; - -static PyObject * -pg_endcopy(pgobject * self, PyObject * args) -{ - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method endcopy() takes no parameters."); - return NULL; - } - - /* ends direct copy */ - if (PQendcopy(self->cnx)) - { - PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx)); - return NULL; - } - Py_INCREF(Py_None); - return Py_None; -} -#endif /* DIRECT_ACCESS */ - - -static PyObject * -pgquery_print(pgqueryobject * self, FILE *fp, int flags) -{ - print_result(fp, self->last_result); - return 0; -} - -static PyObject * -pgquery_repr(pgqueryobject * self) -{ - return PyString_FromString(""); -} - -/* insert table */ -static char pg_inserttable__doc__[] = -"inserttable(string, list) -- insert list in table. The fields in the " -"list must be in the same order as in the table."; - -static PyObject * -pg_inserttable(pgobject * self, PyObject * args) -{ - PGresult *result; - char *table, - *buffer, - *bufpt; - size_t bufsiz; - PyObject *list, - *sublist, - *item; - PyObject *(*getitem) (PyObject *, Py_ssize_t); - PyObject *(*getsubitem) (PyObject *, Py_ssize_t); - int i, - j, - m, - n; - - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "sO:filter", &table, &list)) - { - PyErr_SetString(PyExc_TypeError, - "inserttable(table, content), with table (string) " - "and content (list)."); - return NULL; - } - - /* checks list type */ - if (PyTuple_Check(list)) - { - m = PyTuple_Size(list); - getitem = PyTuple_GetItem; - } - else if (PyList_Check(list)) - { - m = PyList_Size(list); - getitem = PyList_GetItem; - } - else - { - PyErr_SetString(PyExc_TypeError, - "second arg must be some kind of array."); - return NULL; - } - - /* allocate buffer */ - if (!(buffer = malloc(MAX_BUFFER_SIZE))) - { - PyErr_SetString(PyExc_MemoryError, - "can't allocate insert buffer."); - return NULL; - } - - /* starts query */ - sprintf(buffer, "copy %s from stdin", table); - - Py_BEGIN_ALLOW_THREADS - result = PQexec(self->cnx, buffer); - Py_END_ALLOW_THREADS - - if (!result) - { - free(buffer); - PyErr_SetString(PyExc_ValueError, PQerrorMessage(self->cnx)); - return NULL; - } - - PQclear(result); - - n = 0; /* not strictly necessary but avoids warning */ - - /* feed table */ - for (i = 0; i < m; i++) - { - sublist = getitem(list, i); - if (PyTuple_Check(sublist)) - { - j = PyTuple_Size(sublist); - getsubitem = PyTuple_GetItem; - } - else if (PyList_Check(sublist)) - { - j = PyList_Size(sublist); - getsubitem = PyList_GetItem; - } - else - { - PyErr_SetString(PyExc_TypeError, - "second arg must contain some kind of arrays."); - return NULL; - } - if (i) - { - if (j != n) - { - free(buffer); - PyErr_SetString(PyExc_TypeError, - "arrays contained in second arg must have same size."); - return NULL; - } - } - else - { - n = j; /* never used before this assignment */ - } - - /* builds insert line */ - bufpt = buffer; - bufsiz = MAX_BUFFER_SIZE - 1; - - for (j = 0; j < n; j++) - { - if (j) - { - *bufpt++ = '\t'; --bufsiz; - } - - item = getsubitem(sublist, j); - - /* convert item to string and append to buffer */ - if (item == Py_None) - { - if (bufsiz > 2) - { - *bufpt++ = '\\'; *bufpt++ = 'N'; - bufsiz -= 2; - } - else - bufsiz = 0; - } - else if (PyString_Check(item)) - { - const char* t = PyString_AS_STRING(item); - while (*t && bufsiz) - { - if (*t == '\\' || *t == '\t' || *t == '\n') - { - *bufpt++ = '\\'; --bufsiz; - if (!bufsiz) break; - } - *bufpt++ = *t++; --bufsiz; - } - } - else if (PyInt_Check(item) || PyLong_Check(item)) - { - PyObject* s = PyObject_Str(item); - - if (s == NULL) - { - free(buffer); - PyErr_SetString(PyExc_MemoryError, - "fail to compute a string representation."); - return NULL; - } - - const char* t = PyString_AsString(s); - while (*t && bufsiz) - { - *bufpt++ = *t++; --bufsiz; - } - Py_DECREF(s); - } - else - { - PyObject* s = PyObject_Repr(item); - const char* t = PyString_AsString(s); - while (*t && bufsiz) - { - if (*t == '\\' || *t == '\t' || *t == '\n') - { - *bufpt++ = '\\'; --bufsiz; - if (!bufsiz) break; - } - *bufpt++ = *t++; --bufsiz; - } - Py_DECREF(s); - } - - if (bufsiz <= 0) - { - free(buffer); - PyErr_SetString(PyExc_MemoryError, - "insert buffer overflow."); - return NULL; - } - - } - - *bufpt++ = '\n'; *bufpt = '\0'; - - /* sends data */ - if (PQputline(self->cnx, buffer)) - { - PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx)); - PQendcopy(self->cnx); - free(buffer); - return NULL; - } - } - - /* ends query */ - if (PQputline(self->cnx, "\\.\n")) - { - PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx)); - PQendcopy(self->cnx); - free(buffer); - return NULL; - } - - if (PQendcopy(self->cnx)) - { - PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx)); - free(buffer); - return NULL; - } - - free(buffer); - - /* no error : returns nothing */ - Py_INCREF(Py_None); - return Py_None; -} - -/* get transaction state */ -static char pg_transaction__doc__[] = -"Returns the current transaction status."; - -static PyObject * -pg_transaction(pgobject * self, PyObject * args) -{ - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method transaction() takes no parameters."); - return NULL; - } - - return PyInt_FromLong(PQtransactionStatus(self->cnx)); -} - -/* get parameter setting */ -static char pg_parameter__doc__[] = -"Looks up a current parameter setting."; - -static PyObject * -pg_parameter(pgobject * self, PyObject * args) -{ - const char *name; - - if (!self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* get query args */ - if (!PyArg_ParseTuple(args, "s", &name)) - { - PyErr_SetString(PyExc_TypeError, "parameter(name), with name (string)."); - return NULL; - } - - name = PQparameterStatus(self->cnx, name); - - if (name) - return PyString_FromString(name); - - /* unknown parameter, return None */ - Py_INCREF(Py_None); - return Py_None; -} - -/* escape string */ -static char pg_escape_string__doc__[] = -"pg_escape_string(str) -- escape a string for use within SQL."; - -static PyObject * -pg_escape_string(pgobject *self, PyObject *args) { - char *from; /* our string argument */ - char *to=NULL; /* the result */ - int from_length; /* length of string */ - int to_length; /* length of result */ - PyObject *ret; /* string object to return */ - - if (!PyArg_ParseTuple(args, "s#", &from, &from_length)) - return NULL; - to_length = 2*from_length + 1; - if (to_length < from_length) { /* overflow */ - to_length = from_length; - from_length = (from_length - 1)/2; - } - to = (char *)malloc(to_length); - to_length = (int)PQescapeStringConn(self->cnx, - to, from, (size_t)from_length, NULL); - ret = Py_BuildValue("s#", to, to_length); - if (to) - free(to); - if (!ret) /* pass on exception */ - return NULL; - return ret; -} - -/* escape bytea */ -static char pg_escape_bytea__doc__[] = -"pg_escape_bytea(data) -- escape binary data for use within SQL as type bytea."; - -static PyObject * -pg_escape_bytea(pgobject *self, PyObject *args) { - unsigned char *from; /* our string argument */ - unsigned char *to; /* the result */ - int from_length; /* length of string */ - size_t to_length; /* length of result */ - PyObject *ret; /* string object to return */ - - if (!PyArg_ParseTuple(args, "s#", &from, &from_length)) - return NULL; - to = PQescapeByteaConn(self->cnx, from, (int)from_length, &to_length); - ret = Py_BuildValue("s", to); - if (to) - PQfreemem((void *)to); - if (!ret) /* pass on exception */ - return NULL; - return ret; -} - -#ifdef LARGE_OBJECTS -/* creates large object */ -static char pg_locreate__doc__[] = -"locreate() -- creates a new large object in the database."; - -static PyObject * -pg_locreate(pgobject * self, PyObject * args) -{ - int mode; - Oid lo_oid; - - /* checks validity */ - if (!check_cnx_obj(self)) - return NULL; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "i", &mode)) - { - PyErr_SetString(PyExc_TypeError, - "locreate(mode), with mode (integer)."); - return NULL; - } - - /* creates large object */ - lo_oid = lo_creat(self->cnx, mode); - if (lo_oid == 0) - { - PyErr_SetString(OperationalError, "can't create large object."); - return NULL; - } - - return (PyObject *) pglarge_new(self, lo_oid); -} - -/* init from already known oid */ -static char pg_getlo__doc__[] = -"getlo(long) -- create a large object instance for the specified oid."; - -static PyObject * -pg_getlo(pgobject * self, PyObject * args) -{ - int lo_oid; - - /* checks validity */ - if (!check_cnx_obj(self)) - return NULL; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "i", &lo_oid)) - { - PyErr_SetString(PyExc_TypeError, "getlo(oid), with oid (integer)."); - return NULL; - } - - if (!lo_oid) - { - PyErr_SetString(PyExc_ValueError, "the object oid can't be null."); - return NULL; - } - - /* creates object */ - return (PyObject *) pglarge_new(self, lo_oid); -} - -/* import unix file */ -static char pg_loimport__doc__[] = -"loimport(string) -- create a new large object from specified file."; - -static PyObject * -pg_loimport(pgobject * self, PyObject * args) -{ - char *name; - Oid lo_oid; - - /* checks validity */ - if (!check_cnx_obj(self)) - return NULL; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "s", &name)) - { - PyErr_SetString(PyExc_TypeError, "loimport(name), with name (string)."); - return NULL; - } - - /* imports file and checks result */ - lo_oid = lo_import(self->cnx, name); - if (lo_oid == 0) - { - PyErr_SetString(OperationalError, "can't create large object."); - return NULL; - } - - return (PyObject *) pglarge_new(self, lo_oid); -} -#endif /* LARGE_OBJECTS */ - -#ifdef HANDLE_NOTICES - -/* fetch accumulated backend notices */ -static char pg_notices__doc__[] = - "notices() -- returns and clears the list of currently accumulated backend notices for the connection."; - -static void -notice_processor(void *arg, const char *message) -{ - /* require a GIL to avoid multi threads access this code */ - PyGILState_STATE gstate = PyGILState_Ensure(); - pgobject *self = (pgobject *) arg; - PyObject *pymsg; - - if (!self->notices || !PyList_Check(self->notices)) - { - self->notices = PyList_New(0); - if (!self->notices) - return; - } - - pymsg = PyString_FromString(message); - PyList_Append(self->notices, pymsg); - Py_DECREF(pymsg); - - /* If we are at capacity, remove the head element */ - if (PyList_Size(self->notices) > MAX_BUFFERED_NOTICES) - PySequence_DelItem(self->notices, 0); - PyGILState_Release(gstate); -} - -static PyObject * -pg_notices(pgobject *self, PyObject *args) -{ - PyObject *retval; - - if (self->notices) - { - int len = PyList_Size(self->notices); - - retval = PyList_GetSlice(self->notices, 0, len); - } - else - retval = PyList_New(0); - - Py_CLEAR(self->notices); - return retval; -} - -#endif /* HANDLE_NOTICES */ - -/* connection object methods */ -static struct PyMethodDef pgobj_methods[] = { - {"source", (PyCFunction) pg_source, METH_VARARGS, pg_source__doc__}, - {"query", (PyCFunction) pg_query, METH_VARARGS, pg_query__doc__}, - {"reset", (PyCFunction) pg_reset, METH_VARARGS, pg_reset__doc__}, - {"cancel", (PyCFunction) pg_cancel, METH_VARARGS, pg_cancel__doc__}, - {"close", (PyCFunction) pg_close, METH_VARARGS, pg_close__doc__}, - {"fileno", (PyCFunction) pg_fileno, METH_VARARGS, pg_fileno__doc__}, - {"getnotify", (PyCFunction) pg_getnotify, METH_VARARGS, - pg_getnotify__doc__}, - {"inserttable", (PyCFunction) pg_inserttable, METH_VARARGS, - pg_inserttable__doc__}, - {"transaction", (PyCFunction) pg_transaction, METH_VARARGS, - pg_transaction__doc__}, - {"parameter", (PyCFunction) pg_parameter, METH_VARARGS, - pg_parameter__doc__}, - {"escape_string", (PyCFunction) pg_escape_string, METH_VARARGS, - pg_escape_string__doc__}, - {"escape_bytea", (PyCFunction) pg_escape_bytea, METH_VARARGS, - pg_escape_bytea__doc__}, - -#ifdef DIRECT_ACCESS - {"putline", (PyCFunction) pg_putline, 1, pg_putline__doc__}, - {"getline", (PyCFunction) pg_getline, 1, pg_getline__doc__}, - {"endcopy", (PyCFunction) pg_endcopy, 1, pg_endcopy__doc__}, -#endif /* DIRECT_ACCESS */ - -#ifdef LARGE_OBJECTS - {"locreate", (PyCFunction) pg_locreate, 1, pg_locreate__doc__}, - {"getlo", (PyCFunction) pg_getlo, 1, pg_getlo__doc__}, - {"loimport", (PyCFunction) pg_loimport, 1, pg_loimport__doc__}, -#endif /* LARGE_OBJECTS */ - -#ifdef HANDLE_NOTICES - {"notices", (PyCFunction) pg_notices, 1, pg_notices__doc__}, -#endif /* HANDLE_NOTICES */ - - {NULL, NULL} /* sentinel */ -}; - -/* get attribute */ -static PyObject * -pg_getattr(pgobject * self, char *name) -{ - /* - * Although we could check individually, there are only a few - * attributes that don't require a live connection and unless someone - * has an urgent need, this will have to do - */ - - /* first exception - close which returns a different error */ - if (strcmp(name, "close") && !self->cnx) - { - PyErr_SetString(PyExc_TypeError, "Connection is not valid."); - return NULL; - } - - /* list postgreSQL connection fields */ - - /* postmaster host */ - if (!strcmp(name, "host")) - { - char *r = PQhost(self->cnx); - - return r ? PyString_FromString(r) : PyString_FromString("localhost"); - } - - /* postmaster port */ - if (!strcmp(name, "port")) - return PyInt_FromLong(atol(PQport(self->cnx))); - - /* selected database */ - if (!strcmp(name, "db")) - return PyString_FromString(PQdb(self->cnx)); - - /* selected options */ - if (!strcmp(name, "options")) - return PyString_FromString(PQoptions(self->cnx)); - - /* selected postgres tty */ - if (!strcmp(name, "tty")) - return PyString_FromString(PQtty(self->cnx)); - - /* error (status) message */ - if (!strcmp(name, "error")) - return PyString_FromString(PQerrorMessage(self->cnx)); - - /* connection status : 1 - OK, 0 - BAD */ - if (!strcmp(name, "status")) - return PyInt_FromLong(PQstatus(self->cnx) == CONNECTION_OK ? 1 : 0); - - /* provided user name */ - if (!strcmp(name, "user")) - return PyString_FromString(PQuser(self->cnx)); - - /* protocol version */ - if (!strcmp(name, "protocol_version")) - return PyInt_FromLong(PQprotocolVersion(self->cnx)); - - /* backend version */ - if (!strcmp(name, "server_version")) -#if PG_VERSION_NUM < 80000 - return PyInt_FromLong(PG_VERSION_NUM); -#else - return PyInt_FromLong(PQserverVersion(self->cnx)); -#endif - - /* attributes list */ - if (!strcmp(name, "__members__")) - { - PyObject *list = PyList_New(10); - - if (list) - { - PyList_SET_ITEM(list, 0, PyString_FromString("host")); - PyList_SET_ITEM(list, 1, PyString_FromString("port")); - PyList_SET_ITEM(list, 2, PyString_FromString("db")); - PyList_SET_ITEM(list, 3, PyString_FromString("options")); - PyList_SET_ITEM(list, 4, PyString_FromString("tty")); - PyList_SET_ITEM(list, 5, PyString_FromString("error")); - PyList_SET_ITEM(list, 6, PyString_FromString("status")); - PyList_SET_ITEM(list, 7, PyString_FromString("user")); - PyList_SET_ITEM(list, 8, PyString_FromString("protocol_version")); - PyList_SET_ITEM(list, 9, PyString_FromString("server_version")); - } - - return list; - } - - return Py_FindMethod(pgobj_methods, (PyObject *) self, name); -} - -/* object type definition */ -staticforward PyTypeObject PgType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "pgobject", /* tp_name */ - sizeof(pgobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor) pg_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - (getattrfunc) pg_getattr, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ -}; - - -/* query object methods */ -static struct PyMethodDef pgquery_methods[] = { - {"getresult", (PyCFunction) pgquery_getresult, METH_VARARGS, - pgquery_getresult__doc__}, - {"dictresult", (PyCFunction) pgquery_dictresult, METH_VARARGS, - pgquery_dictresult__doc__}, - {"fieldname", (PyCFunction) pgquery_fieldname, METH_VARARGS, - pgquery_fieldname__doc__}, - {"fieldnum", (PyCFunction) pgquery_fieldnum, METH_VARARGS, - pgquery_fieldnum__doc__}, - {"listfields", (PyCFunction) pgquery_listfields, METH_VARARGS, - pgquery_listfields__doc__}, - {"ntuples", (PyCFunction) pgquery_ntuples, METH_VARARGS, - pgquery_ntuples__doc__}, - {NULL, NULL} -}; - -/* gets query object attributes */ -static PyObject * -pgquery_getattr(pgqueryobject * self, char *name) -{ - /* list postgreSQL connection fields */ - return Py_FindMethod(pgquery_methods, (PyObject *) self, name); -} - -/* query type definition */ -staticforward PyTypeObject PgQueryType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "pgqueryobject", /* tp_name */ - sizeof(pgqueryobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor) pgquery_dealloc, /* tp_dealloc */ - (printfunc) pgquery_print, /* tp_print */ - (getattrfunc) pgquery_getattr, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc) pgquery_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ -}; - - -/* --------------------------------------------------------------------- */ - -/* MODULE FUNCTIONS */ - -/* escape string */ -static char escape_string__doc__[] = -"escape_string(str) -- escape a string for use within SQL."; - -static PyObject * -escape_string(PyObject *self, PyObject *args) { - char *from; /* our string argument */ - char *to=NULL; /* the result */ - int from_length; /* length of string */ - int to_length; /* length of result */ - PyObject *ret; /* string object to return */ - - if (!PyArg_ParseTuple(args, "s#", &from, &from_length)) - return NULL; - to_length = 2*from_length + 1; - if (to_length < from_length) { /* overflow */ - to_length = from_length; - from_length = (from_length - 1)/2; - } - to = (char *)malloc(to_length); - to_length = (int)PQescapeString(to, from, (size_t)from_length); - ret = Py_BuildValue("s#", to, to_length); - if (to) - free(to); - if (!ret) /* pass on exception */ - return NULL; - return ret; -} - -/* escape bytea */ -static char escape_bytea__doc__[] = -"escape_bytea(data) -- escape binary data for use within SQL as type bytea."; - -static PyObject * -escape_bytea(PyObject *self, PyObject *args) { - unsigned char *from; /* our string argument */ - unsigned char *to; /* the result */ - int from_length; /* length of string */ - size_t to_length; /* length of result */ - PyObject *ret; /* string object to return */ - - if (!PyArg_ParseTuple(args, "s#", &from, &from_length)) - return NULL; - to = PQescapeBytea(from, (int)from_length, &to_length); - ret = Py_BuildValue("s", to); - if (to) - PQfreemem((void *)to); - if (!ret) /* pass on exception */ - return NULL; - return ret; -} - -/* unescape bytea */ -static char unescape_bytea__doc__[] = -"unescape_bytea(str) -- unescape bytea data that has been retrieved as text."; - -static PyObject -*unescape_bytea(PyObject *self, PyObject *args) { - unsigned char *from; /* our string argument */ - unsigned char *to; /* the result */ - size_t to_length; /* length of result string */ - PyObject *ret; /* string object to return */ - - if (!PyArg_ParseTuple(args, "s", &from)) - return NULL; - to = PQunescapeBytea(from, &to_length); - ret = Py_BuildValue("s#", to, (int)to_length); - if (to) - PQfreemem((void *)to); - if (!ret) /* pass on exception */ - return NULL; - return ret; -} - -/* set decimal */ -static char set_decimal__doc__[] = -"set_decimal(cls) -- set a decimal type to be used for numeric values."; - -static PyObject * -set_decimal(PyObject * self, PyObject * args) -{ - PyObject *ret = NULL; - PyObject *cls; - - if (PyArg_ParseTuple(args, "O", &cls)) - { - if (cls == Py_None) - { - Py_XDECREF(decimal); decimal = NULL; - Py_INCREF(Py_None); ret = Py_None; - } - else if (PyCallable_Check(cls)) - { - Py_XINCREF(cls); Py_XDECREF(decimal); decimal = cls; - Py_INCREF(Py_None); ret = Py_None; - } - else - PyErr_SetString(PyExc_TypeError, "decimal type must be None or callable"); - } - return ret; -} - -#ifdef DEFAULT_VARS - -/* gets default host */ -static char getdefhost__doc__[] = -"get_defhost() -- return default database host."; - -static PyObject * -pggetdefhost(PyObject * self, PyObject * args) -{ - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method get_defhost() takes no parameter."); - return NULL; - } - - Py_XINCREF(pg_default_host); - return pg_default_host; -} - -/* sets default host */ -static char setdefhost__doc__[] = -"set_defhost(string) -- set default database host. Return previous value."; - -static PyObject * -pgsetdefhost(PyObject * self, PyObject * args) -{ - char *temp = NULL; - PyObject *old; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "z", &temp)) - { - PyErr_SetString(PyExc_TypeError, - "set_defhost(name), with name (string/None)."); - return NULL; - } - - /* adjusts value */ - old = pg_default_host; - - if (temp) - pg_default_host = PyString_FromString(temp); - else - { - Py_INCREF(Py_None); - pg_default_host = Py_None; - } - - return old; -} - -/* gets default base */ -static char getdefbase__doc__[] = -"get_defbase() -- return default database name."; - -static PyObject * -pggetdefbase(PyObject * self, PyObject * args) -{ - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method get_defbase() takes no parameter."); - return NULL; - } - - Py_XINCREF(pg_default_base); - return pg_default_base; -} - -/* sets default base */ -static char setdefbase__doc__[] = -"set_defbase(string) -- set default database name. Return previous value"; - -static PyObject * -pgsetdefbase(PyObject * self, PyObject * args) -{ - char *temp = NULL; - PyObject *old; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "z", &temp)) - { - PyErr_SetString(PyExc_TypeError, - "set_defbase(name), with name (string/None)."); - return NULL; - } - - /* adjusts value */ - old = pg_default_base; - - if (temp) - pg_default_base = PyString_FromString(temp); - else - { - Py_INCREF(Py_None); - pg_default_base = Py_None; - } - - return old; -} - -/* gets default options */ -static char getdefopt__doc__[] = -"get_defopt() -- return default database options."; - -static PyObject * -pggetdefopt(PyObject * self, PyObject * args) -{ - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method get_defopt() takes no parameter."); - return NULL; - } - - Py_XINCREF(pg_default_opt); - return pg_default_opt; -} - -/* sets default opt */ -static char setdefopt__doc__[] = -"set_defopt(string) -- set default database options. Return previous value."; - -static PyObject * -pgsetdefopt(PyObject * self, PyObject * args) -{ - char *temp = NULL; - PyObject *old; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "z", &temp)) - { - PyErr_SetString(PyExc_TypeError, - "set_defopt(name), with name (string/None)."); - return NULL; - } - - /* adjusts value */ - old = pg_default_opt; - - if (temp) - pg_default_opt = PyString_FromString(temp); - else - { - Py_INCREF(Py_None); - pg_default_opt = Py_None; - } - - return old; -} - -/* gets default tty */ -static char getdeftty__doc__[] = -"get_deftty() -- return default database debug terminal."; - -static PyObject * -pggetdeftty(PyObject * self, PyObject * args) -{ - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method get_deftty() takes no parameter."); - return NULL; - } - - Py_XINCREF(pg_default_tty); - return pg_default_tty; -} - -/* sets default tty */ -static char setdeftty__doc__[] = -"set_deftty(string) -- set default database debug terminal. " -"Return previous value."; - -static PyObject * -pgsetdeftty(PyObject * self, PyObject * args) -{ - char *temp = NULL; - PyObject *old; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "z", &temp)) - { - PyErr_SetString(PyExc_TypeError, - "set_deftty(name), with name (string/None)."); - return NULL; - } - - /* adjusts value */ - old = pg_default_tty; - - if (temp) - pg_default_tty = PyString_FromString(temp); - else - { - Py_INCREF(Py_None); - pg_default_tty = Py_None; - } - - return old; -} - -/* gets default username */ -static char getdefuser__doc__[] = -"get_defuser() -- return default database username."; - -static PyObject * -pggetdefuser(PyObject * self, PyObject * args) -{ - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method get_defuser() takes no parameter."); - - return NULL; - } - - Py_XINCREF(pg_default_user); - return pg_default_user; -} - -/* sets default username */ -static char setdefuser__doc__[] = -"set_defuser() -- set default database username. Return previous value."; - -static PyObject * -pgsetdefuser(PyObject * self, PyObject * args) -{ - char *temp = NULL; - PyObject *old; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "z", &temp)) - { - PyErr_SetString(PyExc_TypeError, - "set_defuser(name), with name (string/None)."); - return NULL; - } - - /* adjusts value */ - old = pg_default_user; - - if (temp) - pg_default_user = PyString_FromString(temp); - else - { - Py_INCREF(Py_None); - pg_default_user = Py_None; - } - - return old; -} - -/* sets default password */ -static char setdefpasswd__doc__[] = -"set_defpasswd() -- set default database password."; - -static PyObject * -pgsetdefpasswd(PyObject * self, PyObject * args) -{ - char *temp = NULL; - PyObject *old; - - /* gets arguments */ - if (!PyArg_ParseTuple(args, "z", &temp)) - { - PyErr_SetString(PyExc_TypeError, - "set_defpasswd(password), with password (string/None)."); - return NULL; - } - - /* adjusts value */ - old = pg_default_passwd; - - if (temp) - pg_default_passwd = PyString_FromString(temp); - else - { - Py_INCREF(Py_None); - pg_default_passwd = Py_None; - } - - Py_INCREF(Py_None); - return Py_None; -} - -/* gets default port */ -static char getdefport__doc__[] = -"get_defport() -- return default database port."; - -static PyObject * -pggetdefport(PyObject * self, PyObject * args) -{ - /* checks args */ - if (!PyArg_ParseTuple(args, "")) - { - PyErr_SetString(PyExc_TypeError, - "method get_defport() takes no parameter."); - return NULL; - } - - Py_XINCREF(pg_default_port); - return pg_default_port; -} - -/* sets default port */ -static char setdefport__doc__[] = -"set_defport(integer) -- set default database port. Return previous value."; - -static PyObject * -pgsetdefport(PyObject * self, PyObject * args) -{ - long int port = -2; - PyObject *old; - - /* gets arguments */ - if ((!PyArg_ParseTuple(args, "l", &port)) || (port < -1)) - { - PyErr_SetString(PyExc_TypeError, "set_defport(port), with port " - "(positive integer/-1)."); - return NULL; - } - - /* adjusts value */ - old = pg_default_port; - - if (port != -1) - pg_default_port = PyInt_FromLong(port); - else - { - Py_INCREF(Py_None); - pg_default_port = Py_None; - } - - return old; -} -#endif /* DEFAULT_VARS */ - -/* List of functions defined in the module */ - -static struct PyMethodDef pg_methods[] = { - {"connect", (PyCFunction) pgconnect, METH_VARARGS|METH_KEYWORDS, - connect__doc__}, - {"escape_string", (PyCFunction) escape_string, METH_VARARGS, - escape_string__doc__}, - {"escape_bytea", (PyCFunction) escape_bytea, METH_VARARGS, - escape_bytea__doc__}, - {"unescape_bytea", (PyCFunction) unescape_bytea, METH_VARARGS, - unescape_bytea__doc__}, - {"set_decimal", (PyCFunction) set_decimal, METH_VARARGS, - set_decimal__doc__}, - -#ifdef DEFAULT_VARS - {"get_defhost", pggetdefhost, METH_VARARGS, getdefhost__doc__}, - {"set_defhost", pgsetdefhost, METH_VARARGS, setdefhost__doc__}, - {"get_defbase", pggetdefbase, METH_VARARGS, getdefbase__doc__}, - {"set_defbase", pgsetdefbase, METH_VARARGS, setdefbase__doc__}, - {"get_defopt", pggetdefopt, METH_VARARGS, getdefopt__doc__}, - {"set_defopt", pgsetdefopt, METH_VARARGS, setdefopt__doc__}, - {"get_deftty", pggetdeftty, METH_VARARGS, getdeftty__doc__}, - {"set_deftty", pgsetdeftty, METH_VARARGS, setdeftty__doc__}, - {"get_defport", pggetdefport, METH_VARARGS, getdefport__doc__}, - {"set_defport", pgsetdefport, METH_VARARGS, setdefport__doc__}, - {"get_defuser", pggetdefuser, METH_VARARGS, getdefuser__doc__}, - {"set_defuser", pgsetdefuser, METH_VARARGS, setdefuser__doc__}, - {"set_defpasswd", pgsetdefpasswd, METH_VARARGS, setdefpasswd__doc__}, -#endif /* DEFAULT_VARS */ - {NULL, NULL} /* sentinel */ -}; - -static char pg__doc__[] = "Python interface to PostgreSQL DB"; - -/* Initialization function for the module */ -DL_EXPORT(void) -init_pg(void) -{ - PyObject *mod, - *dict, - *v; - - /* Initialize here because some WIN platforms get confused otherwise */ - PglargeType.ob_type = PgType.ob_type = PgQueryType.ob_type = - PgSourceType.ob_type = &PyType_Type; - - /* Create the module and add the functions */ - mod = Py_InitModule4("_pg", pg_methods, pg__doc__, NULL, PYTHON_API_VERSION); - dict = PyModule_GetDict(mod); - - /* Exceptions as defined by DB-API 2.0 */ - Error = PyErr_NewException("pg.Error", PyExc_StandardError, NULL); - PyDict_SetItemString(dict, "Error", Error); - - Warning = PyErr_NewException("pg.Warning", PyExc_StandardError, NULL); - PyDict_SetItemString(dict, "Warning", Warning); - - InterfaceError = PyErr_NewException("pg.InterfaceError", Error, NULL); - PyDict_SetItemString(dict, "InterfaceError", InterfaceError); - - DatabaseError = PyErr_NewException("pg.DatabaseError", Error, NULL); - PyDict_SetItemString(dict, "DatabaseError", DatabaseError); - - InternalError = PyErr_NewException("pg.InternalError", DatabaseError, NULL); - PyDict_SetItemString(dict, "InternalError", InternalError); - - OperationalError = - PyErr_NewException("pg.OperationalError", DatabaseError, NULL); - PyDict_SetItemString(dict, "OperationalError", OperationalError); - - ProgrammingError = - PyErr_NewException("pg.ProgrammingError", DatabaseError, NULL); - PyDict_SetItemString(dict, "ProgrammingError", ProgrammingError); - - IntegrityError = - PyErr_NewException("pg.IntegrityError", DatabaseError, NULL); - PyDict_SetItemString(dict, "IntegrityError", IntegrityError); - - DataError = PyErr_NewException("pg.DataError", DatabaseError, NULL); - PyDict_SetItemString(dict, "DataError", DataError); - - NotSupportedError = - PyErr_NewException("pg.NotSupportedError", DatabaseError, NULL); - PyDict_SetItemString(dict, "NotSupportedError", NotSupportedError); - - /* Make the version available */ - v = PyString_FromString(PyPgVersion); - PyDict_SetItemString(dict, "version", v); - PyDict_SetItemString(dict, "__version__", v); - Py_DECREF(v); - - /* results type for queries */ - PyDict_SetItemString(dict, "RESULT_EMPTY", PyInt_FromLong(RESULT_EMPTY)); - PyDict_SetItemString(dict, "RESULT_DML", PyInt_FromLong(RESULT_DML)); - PyDict_SetItemString(dict, "RESULT_DDL", PyInt_FromLong(RESULT_DDL)); - PyDict_SetItemString(dict, "RESULT_DQL", PyInt_FromLong(RESULT_DQL)); - - /* transaction states */ - PyDict_SetItemString(dict,"TRANS_IDLE",PyInt_FromLong(PQTRANS_IDLE)); - PyDict_SetItemString(dict,"TRANS_ACTIVE",PyInt_FromLong(PQTRANS_ACTIVE)); - PyDict_SetItemString(dict,"TRANS_INTRANS",PyInt_FromLong(PQTRANS_INTRANS)); - PyDict_SetItemString(dict,"TRANS_INERROR",PyInt_FromLong(PQTRANS_INERROR)); - PyDict_SetItemString(dict,"TRANS_UNKNOWN",PyInt_FromLong(PQTRANS_UNKNOWN)); - -#ifdef LARGE_OBJECTS - /* create mode for large objects */ - PyDict_SetItemString(dict, "INV_READ", PyInt_FromLong(INV_READ)); - PyDict_SetItemString(dict, "INV_WRITE", PyInt_FromLong(INV_WRITE)); - - /* position flags for lo_lseek */ - PyDict_SetItemString(dict, "SEEK_SET", PyInt_FromLong(SEEK_SET)); - PyDict_SetItemString(dict, "SEEK_CUR", PyInt_FromLong(SEEK_CUR)); - PyDict_SetItemString(dict, "SEEK_END", PyInt_FromLong(SEEK_END)); -#endif /* LARGE_OBJECTS */ - -#ifdef DEFAULT_VARS - /* prepares default values */ - Py_INCREF(Py_None); - pg_default_host = Py_None; - Py_INCREF(Py_None); - pg_default_base = Py_None; - Py_INCREF(Py_None); - pg_default_opt = Py_None; - Py_INCREF(Py_None); - pg_default_port = Py_None; - Py_INCREF(Py_None); - pg_default_tty = Py_None; - Py_INCREF(Py_None); - pg_default_user = Py_None; - Py_INCREF(Py_None); - pg_default_passwd = Py_None; -#endif /* DEFAULT_VARS */ - - /* Check for errors */ - if (PyErr_Occurred()) - Py_FatalError("can't initialize module _pg"); -} diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/setup.py b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/setup.py deleted file mode 100755 index fccc383ce823..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/setup.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env python -# $Id: setup.py,v 1.27 2008/11/21 17:08:17 cito Exp $ - -"""Setup script for PyGreSQL version 4.0 - -Authors and history: -* PyGreSQL written 1997 by D'Arcy J.M. Cain -* based on code written 1995 by Pascal Andre -* setup script created 2000/04 Mark Alexander -* tweaked 2000/05 Jeremy Hylton -* win32 support 2001/01 by Gerhard Haering -* tweaked 2006/02 and 2008/11 by Christoph Zwerschke - -Prerequisites to be installed: -* Python including devel package (header files and distutils) -* PostgreSQL libs and devel packages (header files of client and server) -* PostgreSQL pg_config tool (usually included in the devel package) - (the Windows installer has it as part of the database server feature) - -Tested with Python 2.5.2 and PostGreSQL 8.3.5. Older version should work -as well, but you will need at least Python 2.3 and PostgreSQL 7.4. - -Use as follows: -python setup.py build # to build the module -python setup.py install # to install it - -You should use MinGW (www.mingw.org) for building on Win32: -python setup.py build -c mingw32 install # use MinGW -Note that Python newer than version 2.3 is using msvcr71 instead of msvcrt -as its common runtime library. So, if you are using MinGW to build PyGreSQL, -you should edit the file "%MinGWpath%/lib/gcc/%MinGWversion%/specs" -and change the entry that reads -lmsvcrt to -lmsvcr71. - -See docs.python.org/doc/install/ for more information on -using distutils to install Python programs. - -""" - -version = "4.0" - -import sys -import os - -if not (2, 2) < sys.version_info[:2] < (3, 0): - raise Exception("PyGreSQL %s requires a Python 2 version" - " newer than 2.2." % version) - -import os -from distutils.core import setup -from distutils.extension import Extension - -def pg_config(s): - """Retrieve information about installed version of PostgreSQL.""" - if os.path.exists("../../../../src/bin/pg_config/pg_config"): - f = os.popen("../../../../src/bin/pg_config/pg_config --%s" % s) - else: - """If a VPATH build, it might not be there. Look other places""" - """It should be the one in the path, because the makefile includes greengage_path.sh """ - f = os.popen("pg_config --%s" % s) - - d = f.readline().strip() - if f.close() is not None: - raise Exception("pg_config tool is not available.") - if not d: - raise Exception("Could not get %s information." % s) - return os.getenv('DESTDIR','')+d - -def mk_include(): - """Create a temporary local include directory. - - The directory will contain a copy of the PostgreSQL server header files, - where all features which are not necessary for PyGreSQL are disabled. - - """ - os.mkdir('include') - for f in os.listdir(pg_include_dir_server): - if not f.endswith('.h'): - continue - d = open(os.path.join(pg_include_dir_server, f)).read() - if f == 'pg_config.h': - d += '\n'.join(('', - '#undef ENABLE_NLS', - '#undef USE_REPL_SNPRINTF', - '#undef USE_SSL', - '#undef USE_ZLIB', - '#undef HAVE_STDINT_H', - '#undef HAVE_SYS_TIME_H', - '#undef HAVE_UNISTD_H', - '#define _CRT_SECURE_NO_WARNINGS 1', - '#define _USE_32BIT_TIME_T 1', - '')) - open(os.path.join('include', f), 'w').write(d) - -def rm_include(): - """Remove the temporary local include directory.""" - if os.path.exists('include'): - for f in os.listdir('include'): - os.remove(os.path.join('include', f)) - os.rmdir('include') - -pg_include_dir = pg_config('includedir') -pg_include_dir_server = pg_config('includedir-server') - -rm_include() -mk_include() - -include_dirs = ['include', pg_include_dir, pg_include_dir_server] - -pg_libdir = pg_config('libdir') -library_dirs = [pg_libdir] - -libraries = ['pq'] -extra_compile_args = ['-O2'] - -if sys.platform == "win32": - include_dirs.append(os.path.join(pg_include_dir_server, 'port/win32')) -elif sys.platform == 'darwin' and sys.maxsize > 2**32: - extra_compile_args.extend(['-arch', os.uname()[4]]) - -setup( - name="PyGreSQL", - version=version, - description="Python PostgreSQL Interfaces", - long_description = ("PyGreSQL is an open-source Python module" - " that interfaces to a PostgreSQL database." - " It embeds the PostgreSQL query library to allow easy use" - " of the powerful PostgreSQL features from a Python script."), - keywords="postgresql database api dbapi", - author="D'Arcy J. M. Cain", - author_email="darcy@PyGreSQL.org", - url="http://www.pygresql.org", - download_url = "ftp://ftp.pygresql.org/pub/distrib/", - platforms = ["any"], - license="Python", - py_modules=['pg', 'pgdb'], - ext_modules=[Extension( - '_pg', ['pgmodule.c'], - include_dirs = include_dirs, - library_dirs = library_dirs, - libraries = libraries, - extra_compile_args = extra_compile_args - )], - classifiers=[ - "Development Status :: 6 - Mature", - "Intended Audience :: Developers", - "License :: OSI Approved :: Python Software Foundation License", - "Operating System :: OS Independent", - "Programming Language :: C", - "Programming Language :: Python", - "Topic :: Database", - "Topic :: Database :: Front-Ends", - "Topic :: Software Development :: Libraries :: Python Modules" - ] -) - -rm_include() diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/advanced.py b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/advanced.py deleted file mode 100755 index 41a5bc457a33..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/advanced.py +++ /dev/null @@ -1,198 +0,0 @@ -#! /usr/bin/env python -# advanced.py - demo of advanced features of PostGres. Some may not be ANSI. -# inspired from the Postgres tutorial -# adapted to Python 1995 by Pascal Andre - -print """ -__________________________________________________________________ -MODULE ADVANCED.PY : ADVANCED POSTGRES SQL COMMANDS TUTORIAL - -This module is designed for being imported from python prompt - -In order to run the samples included here, first create a connection -using : cnx = advanced.DB(...) - -The "..." should be replaced with whatever arguments you need to open an -existing database. Usually all you need is the name of the database and, -in fact, if it is the same as your login name, you can leave it empty. - -then start the demo with: advanced.demo(cnx) -__________________________________________________________________ -""" - -from pg import DB -import sys - -# waits for a key -def wait_key(): - print "Press " - sys.stdin.read(1) - -# inheritance features -def inherit_demo(pgcnx): - print "-----------------------------" - print "-- Inheritance:" - print "-- a table can inherit from zero or more tables. A query" - print "-- can reference either all rows of a table or all rows " - print "-- of a table plus all of its descendants." - print "-----------------------------" - print - print "-- For example, the capitals table inherits from cities table." - print "-- (It inherits all data fields from cities.)" - print - print "CREATE TABLE cities (" - print " name text," - print " population float8," - print " altitude int" - print ")" - print - print "CREATE TABLE capitals (" - print " state varchar(2)" - print ") INHERITS (cities)" - pgcnx.query("""CREATE TABLE cities ( - name text, - population float8, - altitude int)""") - pgcnx.query("""CREATE TABLE capitals ( - state varchar(2)) INHERITS (cities)""") - wait_key() - print - print "-- now, let's populate the tables" - print - print "INSERT INTO cities VALUES ('San Francisco', 7.24E+5, 63)" - print "INSERT INTO cities VALUES ('Las Vegas', 2.583E+5, 2174)" - print "INSERT INTO cities VALUES ('Mariposa', 1200, 1953)" - print - print "INSERT INTO capitals VALUES ('Sacramento', 3.694E+5, 30, 'CA')" - print "INSERT INTO capitals VALUES ('Madison', 1.913E+5, 845, 'WI')" - print - pgcnx.query("INSERT INTO cities VALUES ('San Francisco', 7.24E+5, 63)") - pgcnx.query("INSERT INTO cities VALUES ('Las Vegas', 2.583E+5, 2174)") - pgcnx.query("INSERT INTO cities VALUES ('Mariposa', 1200, 1953)") - pgcnx.query("INSERT INTO capitals VALUES ('Sacramento',3.694E+5,30,'CA')") - pgcnx.query("INSERT INTO capitals VALUES ('Madison', 1.913E+5, 845, 'WI')") - print - print "SELECT * FROM cities" - print pgcnx.query("SELECT * FROM cities") - print "SELECT * FROM capitals" - print pgcnx.query("SELECT * FROM capitals") - print - print "-- like before, a regular query references rows of the base" - print "-- table only" - print - print "SELECT name, altitude" - print "FROM cities" - print "WHERE altitude > 500;" - print pgcnx.query("""SELECT name, altitude - FROM cities - WHERE altitude > 500""") - print - print "-- on the other hand, you can find all cities, including " - print "-- capitals, that are located at an altitude of 500 'ft " - print "-- or higher by:" - print - print "SELECT c.name, c.altitude" - print "FROM cities* c" - print "WHERE c.altitude > 500" - print pgcnx.query("""SELECT c.name, c.altitude - FROM cities* c - WHERE c.altitude > 500""") - -# arrays attributes -def array_demo(pgcnx): - print "----------------------" - print "-- Arrays:" - print "-- attributes can be arrays of base types or user-defined " - print "-- types" - print "----------------------" - print - print "CREATE TABLE sal_emp (" - print " name text," - print " pay_by_quarter int4[]," - print " pay_by_extra_quarter int8[]," - print " schedule text[][]" - print ")" - pgcnx.query("""CREATE TABLE sal_emp ( - name text, - pay_by_quarter int4[], - pay_by_extra_quarter int8[], - schedule text[][])""") - wait_key() - print - print "-- insert instances with array attributes. " - print " Note the use of braces" - print - print "INSERT INTO sal_emp VALUES (" - print " 'Bill'," - print " '{10000,10000,10000,10000}'," - print " '{9223372036854775800,9223372036854775800,9223372036854775800}'," - print " '{{\"meeting\", \"lunch\"}, {}}')" - print - print "INSERT INTO sal_emp VALUES (" - print " 'Carol'," - print " '{20000,25000,25000,25000}'," - print " '{9223372036854775807,9223372036854775807,9223372036854775807}'," - print " '{{\"talk\", \"consult\"}, {\"meeting\"}}')" - print - pgcnx.query("""INSERT INTO sal_emp VALUES ( - 'Bill', '{10000,10000,10000,10000}', - '{9223372036854775800,9223372036854775800,9223372036854775800}', - '{{\"meeting\", \"lunch\"}, {}}')""") - pgcnx.query("""INSERT INTO sal_emp VALUES ( - 'Carol', '{20000,25000,25000,25000}', - '{9223372036854775807,9223372036854775807,9223372036854775807}', - '{{\"talk\", \"consult\"}, {\"meeting\"}}')""") - wait_key() - print - print "----------------------" - print "-- queries on array attributes" - print "----------------------" - print - print "SELECT name FROM sal_emp WHERE" - print " sal_emp.pay_by_quarter[1] <> sal_emp.pay_by_quarter[2]" - print - print pgcnx.query("""SELECT name FROM sal_emp WHERE - sal_emp.pay_by_quarter[1] <> sal_emp.pay_by_quarter[2]""") - print - print pgcnx.query("""SELECT name FROM sal_emp WHERE - sal_emp.pay_by_extra_quarter[1] <> sal_emp.pay_by_extra_quarter[2]""") - print - print "-- retrieve third quarter pay of all employees" - print - print "SELECT sal_emp.pay_by_quarter[3] FROM sal_emp" - print - print pgcnx.query("SELECT sal_emp.pay_by_quarter[3] FROM sal_emp") - print - print "-- retrieve third quarter extra pay of all employees" - print - print "SELECT sal_emp.pay_by_extra_quarter[3] FROM sal_emp" - print pgcnx.query("SELECT sal_emp.pay_by_extra_quarter[3] FROM sal_emp") - print - print "-- retrieve first two quarters of extra quarter pay of all employees" - print - print "SELECT sal_emp.pay_by_extra_quarter[1:2] FROM sal_emp" - print - print pgcnx.query("SELECT sal_emp.pay_by_extra_quarter[1:2] FROM sal_emp") - print - print "-- select subarrays" - print - print "SELECT sal_emp.schedule[1:2][1:1] FROM sal_emp WHERE" - print " sal_emp.name = 'Bill'" - print pgcnx.query("SELECT sal_emp.schedule[1:2][1:1] FROM sal_emp WHERE " \ - "sal_emp.name = 'Bill'") - -# base cleanup -def demo_cleanup(pgcnx): - print "-- clean up (you must remove the children first)" - print "DROP TABLE sal_emp" - print "DROP TABLE capitals" - print "DROP TABLE cities;" - pgcnx.query("DROP TABLE sal_emp") - pgcnx.query("DROP TABLE capitals") - pgcnx.query("DROP TABLE cities") - -# main demo function -def demo(pgcnx): - inherit_demo(pgcnx) - array_demo(pgcnx) - demo_cleanup(pgcnx) diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/basics.py b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/basics.py deleted file mode 100755 index 98a7f86a90be..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/basics.py +++ /dev/null @@ -1,296 +0,0 @@ -#! /usr/bin/env python -# basics.py - basic SQL commands tutorial -# inspired from the Postgres95 tutorial -# adapted to Python 1995 by Pascal ANDRE - -print """ -__________________________________________________________________ -MODULE BASICS.PY : BASIC POSTGRES SQL COMMANDS TUTORIAL - -This module is designed for being imported from python prompt - -In order to run the samples included here, first create a connection -using : cnx = basics.DB(...) - -The "..." should be replaced with whatever arguments you need to open an -existing database. Usually all you need is the name of the database and, -in fact, if it is the same as your login name, you can leave it empty. - -then start the demo with: basics.demo(cnx) -__________________________________________________________________ -""" - -from pg import DB -import sys - -# waits for a key -def wait_key(): - print "Press " - sys.stdin.read(1) - -# table creation commands -def create_table(pgcnx): - print "-----------------------------" - print "-- Creating a table:" - print "-- a CREATE TABLE is used to create base tables. POSTGRES" - print "-- SQL has its own set of built-in types. (Note that" - print "-- keywords are case-insensitive but identifiers are " - print "-- case-sensitive.)" - print "-----------------------------" - print - print "Sending query :" - print "CREATE TABLE weather (" - print " city varchar(80)," - print " temp_lo int," - print " temp_hi int," - print " prcp float8," - print " date date" - print ")" - pgcnx.query("""CREATE TABLE weather (city varchar(80), temp_lo int, - temp_hi int, prcp float8, date date)""") - print - print "Sending query :" - print "CREATE TABLE cities (" - print " name varchar(80)," - print " location point" - print ")" - pgcnx.query("""CREATE TABLE cities ( - name varchar(80), - location point)""") - -# data insertion commands -def insert_data(pgcnx): - print "-----------------------------" - print "-- Inserting data:" - print "-- an INSERT statement is used to insert a new row into" - print "-- a table. There are several ways you can specify what" - print "-- columns the data should go to." - print "-----------------------------" - print - print "-- 1. the simplest case is when the list of value correspond to" - print "-- the order of the columns specified in CREATE TABLE." - print - print "Sending query :" - print "INSERT INTO weather " - print " VALUES ('San Francisco', 46, 50, 0.25, '11/27/1994')" - pgcnx.query("""INSERT INTO weather - VALUES ('San Francisco', 46, 50, 0.25, '11/27/1994')""") - print - print "Sending query :" - print "INSERT INTO cities " - print " VALUES ('San Francisco', '(-194.0, 53.0)')" - pgcnx.query("""INSERT INTO cities - VALUES ('San Francisco', '(-194.0, 53.0)')""") - print - wait_key() - print "-- 2. you can also specify what column the values correspond " - print " to. (The columns can be specified in any order. You may " - print " also omit any number of columns. eg. unknown precipitation" - print " below)" - print "Sending query :" - print "INSERT INTO weather (city, temp_lo, temp_hi, prcp, date)" - print " VALUES ('San Francisco', 43, 57, 0.0, '11/29/1994')" - pgcnx.query("INSERT INTO weather (date, city, temp_hi, temp_lo)" \ - "VALUES ('11/29/1994', 'Hayward', 54, 37)") - -# direct selection commands -def select_data1(pgcnx): - print "-----------------------------" - print "-- Retrieving data:" - print "-- a SELECT statement is used for retrieving data. The " - print "-- basic syntax is:" - print "-- SELECT columns FROM tables WHERE predicates" - print "-----------------------------" - print - print "-- a simple one would be the query:" - print "SELECT * FROM weather" - print - print "The result is :" - q = pgcnx.query("SELECT * FROM weather") - print q - print - print "-- you may also specify expressions in the target list (the " - print "-- 'AS column' specifies the column name of the result. It is " - print "-- optional.)" - print "The query :" - print " SELECT city, (temp_hi+temp_lo)/2 AS temp_avg, date " - print " FROM weather" - print "Gives :" - print pgcnx.query("""SELECT city, (temp_hi+temp_lo)/2 - AS temp_avg, date FROM weather""") - print - print "-- if you want to retrieve rows that satisfy certain condition" - print "-- (ie. a restriction), specify the condition in WHERE. The " - print "-- following retrieves the weather of San Francisco on rainy " - print "-- days." - print "SELECT *" - print "FROM weather" - print "WHERE city = 'San Francisco' " - print " and prcp > 0.0" - print pgcnx.query("""SELECT * FROM weather WHERE city = 'San Francisco' - AND prcp > 0.0""") - print - print "-- here is a more complicated one. Duplicates are removed when " - print "-- DISTINCT is specified. ORDER BY specifies the column to sort" - print "-- on. (Just to make sure the following won't confuse you, " - print "-- DISTINCT and ORDER BY can be used separately.)" - print "SELECT DISTINCT city" - print "FROM weather" - print "ORDER BY city;" - print pgcnx.query("SELECT DISTINCT city FROM weather ORDER BY city") - -# selection to a temporary table -def select_data2(pgcnx): - print "-----------------------------" - print "-- Retrieving data into other classes:" - print "-- a SELECT ... INTO statement can be used to retrieve " - print "-- data into another class." - print "-----------------------------" - print - print "The query :" - print "SELECT * INTO TABLE temptab " - print "FROM weather" - print "WHERE city = 'San Francisco' " - print " and prcp > 0.0" - pgcnx.query("""SELECT * INTO TABLE temptab FROM weather - WHERE city = 'San Francisco' and prcp > 0.0""") - print "Fills the table temptab, that can be listed with :" - print "SELECT * from temptab" - print pgcnx.query("SELECT * from temptab") - -# aggregate creation commands -def create_aggregate(pgcnx): - print "-----------------------------" - print "-- Aggregates" - print "-----------------------------" - print - print "Let's consider the query :" - print "SELECT max(temp_lo)" - print "FROM weather;" - print pgcnx.query("SELECT max(temp_lo) FROM weather") - print - print "-- Aggregate with GROUP BY" - print "SELECT city, max(temp_lo)" - print "FROM weather " - print "GROUP BY city;" - print pgcnx.query( """SELECT city, max(temp_lo) - FROM weather GROUP BY city""") - -# table join commands -def join_table(pgcnx): - print "-----------------------------" - print "-- Joining tables:" - print "-- queries can access multiple tables at once or access" - print "-- the same table in such a way that multiple instances" - print "-- of the table are being processed at the same time." - print "-----------------------------" - print - print "-- suppose we want to find all the records that are in the " - print "-- temperature range of other records. W1 and W2 are aliases " - print "--for weather." - print - print "SELECT W1.city, W1.temp_lo, W1.temp_hi, " - print " W2.city, W2.temp_lo, W2.temp_hi" - print "FROM weather W1, weather W2" - print "WHERE W1.temp_lo < W2.temp_lo " - print " and W1.temp_hi > W2.temp_hi" - print - print pgcnx.query("""SELECT W1.city, W1.temp_lo, W1.temp_hi, - W2.city, W2.temp_lo, W2.temp_hi FROM weather W1, weather W2 - WHERE W1.temp_lo < W2.temp_lo and W1.temp_hi > W2.temp_hi""") - print - print "-- let's join two tables. The following joins the weather table" - print "-- and the cities table." - print - print "SELECT city, location, prcp, date" - print "FROM weather, cities" - print "WHERE name = city" - print - print pgcnx.query("""SELECT city, location, prcp, date FROM weather, cities - WHERE name = city""") - print - print "-- since the column names are all different, we don't have to " - print "-- specify the table name. If you want to be clear, you can do " - print "-- the following. They give identical results, of course." - print - print "SELECT w.city, c.location, w.prcp, w.date" - print "FROM weather w, cities c" - print "WHERE c.name = w.city;" - print - print pgcnx.query("""SELECT w.city, c.location, w.prcp, w.date - FROM weather w, cities c WHERE c.name = w.city""") - -# data updating commands -def update_data(pgcnx): - print "-----------------------------" - print "-- Updating data:" - print "-- an UPDATE statement is used for updating data. " - print "-----------------------------" - print - print "-- suppose you discover the temperature readings are all off by" - print "-- 2 degrees as of Nov 28, you may update the data as follow:" - print - print "UPDATE weather" - print " SET temp_hi = temp_hi - 2, temp_lo = temp_lo - 2" - print " WHERE date > '11/28/1994'" - print - pgcnx.query("""UPDATE weather - SET temp_hi = temp_hi - 2, temp_lo = temp_lo - 2 - WHERE date > '11/28/1994'""") - print - print "SELECT * from weather" - print pgcnx.query("SELECT * from weather") - -# data deletion commands -def delete_data(pgcnx): - print "-----------------------------" - print "-- Deleting data:" - print "-- a DELETE statement is used for deleting rows from a " - print "-- table." - print "-----------------------------" - print - print "-- suppose you are no longer interested in the weather of " - print "-- Hayward, you can do the following to delete those rows from" - print "-- the table" - print - print "DELETE FROM weather WHERE city = 'Hayward'" - pgcnx.query("DELETE FROM weather WHERE city = 'Hayward'") - print - print "SELECT * from weather" - print - print pgcnx.query("SELECT * from weather") - print - print "-- you can also delete all the rows in a table by doing the " - print "-- following. (This is different from DROP TABLE which removes " - print "-- the table in addition to the removing the rows.)" - print - print "DELETE FROM weather" - pgcnx.query("DELETE FROM weather") - print - print "SELECT * from weather" - print pgcnx.query("SELECT * from weather") - -# table removal commands -def remove_table(pgcnx): - print "-----------------------------" - print "-- Removing the tables:" - print "-- DROP TABLE is used to remove tables. After you have" - print "-- done this, you can no longer use those tables." - print "-----------------------------" - print - print "DROP TABLE weather, cities, temptab" - pgcnx.query("DROP TABLE weather, cities, temptab") - -# main demo function -def demo(pgcnx): - create_table(pgcnx) - wait_key() - insert_data(pgcnx) - wait_key() - select_data1(pgcnx) - select_data2(pgcnx) - create_aggregate(pgcnx) - join_table(pgcnx) - update_data(pgcnx) - delete_data(pgcnx) - remove_table(pgcnx) diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/func.py b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/func.py deleted file mode 100755 index af2b412bdfc8..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/func.py +++ /dev/null @@ -1,205 +0,0 @@ -# func.py - demonstrate the use of SQL functions -# inspired from the PostgreSQL tutorial -# adapted to Python 1995 by Pascal ANDRE - -print """ -__________________________________________________________________ -MODULE FUNC.PY : SQL FUNCTION DEFINITION TUTORIAL - -This module is designed for being imported from python prompt - -In order to run the samples included here, first create a connection -using : cnx = func.DB(...) - -The "..." should be replaced with whatever arguments you need to open an -existing database. Usually all you need is the name of the database and, -in fact, if it is the same as your login name, you can leave it empty. - -then start the demo with: func.demo(cnx) -__________________________________________________________________ -""" - -from pg import DB -import sys - -# waits for a key -def wait_key(): - print "Press " - sys.stdin.read(1) - -# basic functions declaration -def base_func(pgcnx): - print "-----------------------------" - print "-- Creating SQL Functions on Base Types" - print "-- a CREATE FUNCTION statement lets you create a new " - print "-- function that can be used in expressions (in SELECT, " - print "-- INSERT, etc.). We will start with functions that " - print "-- return values of base types." - print "-----------------------------" - print - print "--" - print "-- let's create a simple SQL function that takes no arguments" - print "-- and returns 1" - print - print "CREATE FUNCTION one() RETURNS int4" - print " AS 'SELECT 1 as ONE' LANGUAGE 'sql'" - pgcnx.query("""CREATE FUNCTION one() RETURNS int4 - AS 'SELECT 1 as ONE' LANGUAGE 'sql'""") - wait_key() - print - print "--" - print "-- functions can be used in any expressions (eg. in the target" - print "-- list or qualifications)" - print - print "SELECT one() AS answer" - print pgcnx.query("SELECT one() AS answer") - print - print "--" - print "-- here's how you create a function that takes arguments. The" - print "-- following function returns the sum of its two arguments:" - print - print "CREATE FUNCTION add_em(int4, int4) RETURNS int4" - print " AS 'SELECT $1 + $2' LANGUAGE 'sql'" - pgcnx.query("""CREATE FUNCTION add_em(int4, int4) RETURNS int4 - AS 'SELECT $1 + $2' LANGUAGE 'sql'""") - print - print "SELECT add_em(1, 2) AS answer" - print pgcnx.query("SELECT add_em(1, 2) AS answer") - -# functions on composite types -def comp_func(pgcnx): - print "-----------------------------" - print "-- Creating SQL Functions on Composite Types" - print "-- it is also possible to create functions that return" - print "-- values of composite types." - print "-----------------------------" - print - print "-- before we create more sophisticated functions, let's " - print "-- populate an EMP table" - print - print "CREATE TABLE EMP (" - print " name text," - print " salary int4," - print " age int4," - print " dept varchar(16)" - print ")" - pgcnx.query("""CREATE TABLE EMP ( - name text, - salary int4, - age int4, - dept varchar(16))""") - print - print "INSERT INTO EMP VALUES ('Sam', 1200, 16, 'toy')" - print "INSERT INTO EMP VALUES ('Claire', 5000, 32, 'shoe')" - print "INSERT INTO EMP VALUES ('Andy', -1000, 2, 'candy')" - print "INSERT INTO EMP VALUES ('Bill', 4200, 36, 'shoe')" - print "INSERT INTO EMP VALUES ('Ginger', 4800, 30, 'candy')" - pgcnx.query("INSERT INTO EMP VALUES ('Sam', 1200, 16, 'toy')") - pgcnx.query("INSERT INTO EMP VALUES ('Claire', 5000, 32, 'shoe')") - pgcnx.query("INSERT INTO EMP VALUES ('Andy', -1000, 2, 'candy')") - pgcnx.query("INSERT INTO EMP VALUES ('Bill', 4200, 36, 'shoe')") - pgcnx.query("INSERT INTO EMP VALUES ('Ginger', 4800, 30, 'candy')") - wait_key() - print - print "-- the argument of a function can also be a tuple. For " - print "-- instance, double_salary takes a tuple of the EMP table" - print - print "CREATE FUNCTION double_salary(EMP) RETURNS int4" - print " AS 'SELECT $1.salary * 2 AS salary' LANGUAGE 'sql'" - pgcnx.query("""CREATE FUNCTION double_salary(EMP) RETURNS int4 - AS 'SELECT $1.salary * 2 AS salary' LANGUAGE 'sql'""") - print - print "SELECT name, double_salary(EMP) AS dream" - print "FROM EMP" - print "WHERE EMP.dept = 'toy'" - print pgcnx.query("""SELECT name, double_salary(EMP) AS dream - FROM EMP WHERE EMP.dept = 'toy'""") - print - print "-- the return value of a function can also be a tuple. However," - print "-- make sure that the expressions in the target list is in the " - print "-- same order as the columns of EMP." - print - print "CREATE FUNCTION new_emp() RETURNS EMP" - print " AS 'SELECT \'None\'::text AS name," - print " 1000 AS salary," - print " 25 AS age," - print " \'none\'::varchar(16) AS dept'" - print " LANGUAGE 'sql'" - pgcnx.query("""CREATE FUNCTION new_emp() RETURNS EMP - AS 'SELECT \\\'None\\\'::text AS name, - 1000 AS salary, - 25 AS age, - \\\'none\\\'::varchar(16) AS dept' - LANGUAGE 'sql'""") - wait_key() - print - print "-- you can then project a column out of resulting the tuple by" - print "-- using the \"function notation\" for projection columns. " - print "-- (ie. bar(foo) is equivalent to foo.bar) Note that we don't" - print "-- support new_emp().name at this moment." - print - print "SELECT name(new_emp()) AS nobody" - print pgcnx.query("SELECT name(new_emp()) AS nobody") - print - print "-- let's try one more function that returns tuples" - print "CREATE FUNCTION high_pay() RETURNS setof EMP" - print " AS 'SELECT * FROM EMP where salary > 1500'" - print " LANGUAGE 'sql'" - pgcnx.query("""CREATE FUNCTION high_pay() RETURNS setof EMP - AS 'SELECT * FROM EMP where salary > 1500' - LANGUAGE 'sql'""") - print - print "SELECT name(high_pay()) AS overpaid" - print pgcnx.query("SELECT name(high_pay()) AS overpaid") - -# function with multiple SQL commands -def mult_func(pgcnx): - print "-----------------------------" - print "-- Creating SQL Functions with multiple SQL statements" - print "-- you can also create functions that do more than just a" - print "-- SELECT." - print "-----------------------------" - print - print "-- you may have noticed that Andy has a negative salary. We'll" - print "-- create a function that removes employees with negative " - print "-- salaries." - print - print "SELECT * FROM EMP" - print pgcnx.query("SELECT * FROM EMP") - print - print "CREATE FUNCTION clean_EMP () RETURNS int4" - print " AS 'DELETE FROM EMP WHERE EMP.salary <= 0" - print " SELECT 1 AS ignore_this'" - print " LANGUAGE 'sql'" - pgcnx.query("CREATE FUNCTION clean_EMP () RETURNS int4 AS 'DELETE FROM EMP WHERE EMP.salary <= 0; SELECT 1 AS ignore_this' LANGUAGE 'sql'") - print - print "SELECT clean_EMP()" - print pgcnx.query("SELECT clean_EMP()") - print - print "SELECT * FROM EMP" - print pgcnx.query("SELECT * FROM EMP") - -# base cleanup -def demo_cleanup(pgcnx): - print "-- remove functions that were created in this file" - print - print "DROP FUNCTION clean_EMP()" - print "DROP FUNCTION high_pay()" - print "DROP FUNCTION new_emp()" - print "DROP FUNCTION add_em(int4, int4)" - print "DROP FUNCTION one()" - print - print "DROP TABLE EMP CASCADE" - pgcnx.query("DROP FUNCTION clean_EMP()") - pgcnx.query("DROP FUNCTION high_pay()") - pgcnx.query("DROP FUNCTION new_emp()") - pgcnx.query("DROP FUNCTION add_em(int4, int4)") - pgcnx.query("DROP FUNCTION one()") - pgcnx.query("DROP TABLE EMP CASCADE") - -# main demo function -def demo(pgcnx): - base_func(pgcnx) - comp_func(pgcnx) - mult_func(pgcnx) - demo_cleanup(pgcnx) diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/syscat.py b/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/syscat.py deleted file mode 100755 index 1ab1d5840c2c..000000000000 --- a/gpMgmt/bin/pythonSrc/PyGreSQL-4.0/tutorial/syscat.py +++ /dev/null @@ -1,149 +0,0 @@ -# syscat.py - parses some system catalogs -# inspired from the PostgreSQL tutorial -# adapted to Python 1995 by Pascal ANDRE - -print """ -__________________________________________________________________ -MODULE SYSCAT.PY : PARSES SOME POSTGRESQL SYSTEM CATALOGS - -This module is designed for being imported from python prompt - -In order to run the samples included here, first create a connection -using : cnx = syscat.DB(...) - -The "..." should be replaced with whatever arguments you need to open an -existing database. Usually all you need is the name of the database and, -in fact, if it is the same as your login name, you can leave it empty. - -then start the demo with: syscat.demo(cnx) - -Some results may be empty, depending on your base status." - -__________________________________________________________________ -""" - -from pg import DB -import sys - -# waits for a key -def wait_key(): - print "Press " - sys.stdin.read(1) - -# lists all simple indices -def list_simple_ind(pgcnx): - result = pgcnx.query("""SELECT bc.relname AS class_name, - ic.relname AS index_name, a.attname - FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a - WHERE i.indrelid = bc.oid AND i.indexrelid = bc.oid - AND i.indkey[0] = a.attnum AND a.attrelid = bc.oid - AND i.indproc = '0'::oid AND a.attisdropped = 'f' - ORDER BY class_name, index_name, attname""") - return result - -# list all user defined attributes and their type in user-defined classes -def list_all_attr(pgcnx): - result = pgcnx.query("""SELECT c.relname, a.attname, t.typname - FROM pg_class c, pg_attribute a, pg_type t - WHERE c.relkind = 'r' and c.relname !~ '^pg_' - AND c.relname !~ '^Inv' and a.attnum > 0 - AND a.attrelid = c.oid and a.atttypid = t.oid - AND a.attisdropped = 'f' - ORDER BY relname, attname""") - return result - -# list all user defined base type -def list_user_base_type(pgcnx): - result = pgcnx.query("""SELECT u.usename, t.typname - FROM pg_type t, pg_user u - WHERE u.usesysid = int2in(int4out(t.typowner)) - AND t.typrelid = '0'::oid and t.typelem = '0'::oid - AND u.usename <> 'postgres' order by usename, typname""") - return result - -# list all right-unary operators -def list_right_unary_operator(pgcnx): - result = pgcnx.query("""SELECT o.oprname AS right_unary, - lt.typname AS operand, result.typname AS return_type - FROM pg_operator o, pg_type lt, pg_type result - WHERE o.oprkind='r' and o.oprleft = lt.oid - AND o.oprresult = result.oid - ORDER BY operand""") - return result - -# list all left-unary operators -def list_left_unary_operator(pgcnx): - result = pgcnx.query("""SELECT o.oprname AS left_unary, - rt.typname AS operand, result.typname AS return_type - FROM pg_operator o, pg_type rt, pg_type result - WHERE o.oprkind='l' AND o.oprright = rt.oid - AND o.oprresult = result.oid - ORDER BY operand""") - return result - -# list all binary operators -def list_binary_operator(pgcnx): - result = pgcnx.query("""SELECT o.oprname AS binary_op, - rt.typname AS right_opr, lt.typname AS left_opr, - result.typname AS return_type - FROM pg_operator o, pg_type rt, pg_type lt, pg_type result - WHERE o.oprkind = 'b' AND o.oprright = rt.oid - AND o.oprleft = lt.oid AND o.oprresult = result.oid""") - return result - -# returns the name, args and return type from all function of lang l -def list_lang_func(pgcnx, l): - result = pgcnx.query("""SELECT p.proname, p.pronargs, t.typname - FROM pg_proc p, pg_language l, pg_type t - WHERE p.prolang = l.oid AND p.prorettype = t.oid - AND l.lanname = '%s' - ORDER BY proname""" % l) - return result - -# lists all the aggregate functions and the type to which they can be applied -def list_agg_func(pgcnx): - result = pgcnx.query("""SELECT p.proname, t.typname - FROM pg_aggregate a, pg_proc p, pg_type t - WHERE a.aggfnoid = p.oid - and p.proargtypes[0] = t.oid - ORDER BY proname, typname""") - return result - -# lists all the operator classes that can be used with each access method as -# well as the operators that can be used with the respective operator classes -def list_op_class(pgcnx): - result = pgcnx.query("""SELECT am.amname, opc.opcname, opr.oprname - FROM pg_am am, pg_amop amop, pg_opclass opc, pg_operator opr - WHERE amop.amopid = am.oid and amop.amopclaid = opc.oid - AND amop.amopopr = opr.oid order by amname, opcname, oprname""") - return result - -# demo function - runs all examples -def demo(pgcnx): - import sys, os - save_stdout = sys.stdout - sys.stdout = os.popen("more", "w") - print "Listing simple indices ..." - print list_simple_ind(pgcnx) - print "Listing all attributes ..." - print list_all_attr(pgcnx) - print "Listing all user-defined base types ..." - print list_user_base_type(pgcnx) - print "Listing all left-unary operators defined ..." - print list_left_unary_operator(pgcnx) - print "Listing all right-unary operators defined ..." - print list_right_unary_operator(pgcnx) - print "Listing all binary operators ..." - print list_binary_operator(pgcnx) - print "Listing C external function linked ..." - print list_lang_func(pgcnx, 'C') - print "Listing C internal functions ..." - print list_lang_func(pgcnx, 'internal') - print "Listing SQL functions defined ..." - print list_lang_func(pgcnx, 'sql') - print "Listing 'aggregate functions' ..." - print list_agg_func(pgcnx) - print "Listing 'operator classes' ..." - print list_op_class(pgcnx) - del sys.stdout - sys.stdout = save_stdout diff --git a/gpMgmt/bin/pythonSrc/PyGreSQL-5.2.5 b/gpMgmt/bin/pythonSrc/PyGreSQL-5.2.5 new file mode 160000 index 000000000000..f7bc4a36aa83 --- /dev/null +++ b/gpMgmt/bin/pythonSrc/PyGreSQL-5.2.5 @@ -0,0 +1 @@ +Subproject commit f7bc4a36aa83b3e9ac2a6ae7d75ca2261bdbc8e3 diff --git a/gpMgmt/sbin/gpgetstatususingtransition.py b/gpMgmt/sbin/gpgetstatususingtransition.py index 5bde9e13ae1a..eb3cf7f27705 100755 --- a/gpMgmt/sbin/gpgetstatususingtransition.py +++ b/gpMgmt/sbin/gpgetstatususingtransition.py @@ -65,8 +65,8 @@ def _get_segment_version(seg): try: if seg.role == gparray.ROLE_PRIMARY: dburl = dbconn.DbURL(hostname=seg.hostname, port=seg.port, dbname="template1") - conn = dbconn.connect(dburl, utility=True) - return dbconn.execSQLForSingleton(conn, "select version()") + with dbconn.connect(dburl, utility=True) as conn: + return dbconn.execSQLForSingleton(conn, "select version()") if seg.role == gparray.ROLE_MIRROR: cmd = base.Command("Try connecting to mirror", diff --git a/gpMgmt/test/behave/mgmt_utils/analyzedb.feature b/gpMgmt/test/behave/mgmt_utils/analyzedb.feature index a673f88a0897..b76706b2fad4 100644 --- a/gpMgmt/test/behave/mgmt_utils/analyzedb.feature +++ b/gpMgmt/test/behave/mgmt_utils/analyzedb.feature @@ -1,4 +1,4 @@ -@analyzedb +@analyzedb @keep_connection Feature: Incrementally analyze the database Scenario: Invalid arguments entered diff --git a/gpMgmt/test/behave/mgmt_utils/environment.py b/gpMgmt/test/behave/mgmt_utils/environment.py index 8cc64a8a4223..5a394ef8aafc 100644 --- a/gpMgmt/test/behave/mgmt_utils/environment.py +++ b/gpMgmt/test/behave/mgmt_utils/environment.py @@ -163,6 +163,9 @@ def after_scenario(context, scenario): if 'gp_bash_functions' in context.feature.tags or 'backup_restore_bashrc' in scenario.effective_tags: restore_bashrc() + if "keep_connection" not in context.feature.tags and hasattr(context, 'conn'): + context.conn.close() + # NOTE: gpconfig after_scenario cleanup is in the step `the gpconfig context is setup` tags_to_skip = ['gpexpand', 'gpaddmirrors', 'gpinitstandby', 'gpconfig', 'gpstop', 'gpinitsystem', 'cross_subnet', diff --git a/gpMgmt/test/behave/mgmt_utils/gpreload.feature b/gpMgmt/test/behave/mgmt_utils/gpreload.feature index dcd4ef59cb1d..7030e4a891dd 100644 --- a/gpMgmt/test/behave/mgmt_utils/gpreload.feature +++ b/gpMgmt/test/behave/mgmt_utils/gpreload.feature @@ -1,4 +1,4 @@ -@gpreload +@gpreload @keep_connection Feature: gpreload behave tests Scenario: Handle case where a table (public.t1) and view (gpreload_schema.t1) have the same name diff --git a/gpMgmt/test/behave/mgmt_utils/minirepro.feature b/gpMgmt/test/behave/mgmt_utils/minirepro.feature index c461a7bdca3a..22cd665138ef 100644 --- a/gpMgmt/test/behave/mgmt_utils/minirepro.feature +++ b/gpMgmt/test/behave/mgmt_utils/minirepro.feature @@ -1,4 +1,4 @@ -@minirepro +@minirepro @keep_connection Feature: Dump minimum database objects that is related to the query @minirepro_UI diff --git a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py index 2f995b7745ba..a0aa648390c2 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/mgmt_utils.py @@ -1213,11 +1213,11 @@ def impl(context, options): query = """select distinct content, hostname from gp_segment_configuration order by content limit 2;""" cursor = dbconn.execSQL(conn, query) - try: - _, master_hostname = cursor.fetchone() - _, segment_hostname = cursor.fetchone() - except: - raise Exception("Did not get two rows from query: %s" % query) + try: + _, master_hostname = cursor.fetchone() + _, segment_hostname = cursor.fetchone() + except: + raise Exception("Did not get two rows from query: %s" % query) # if we have two hosts, assume we're testing on a multinode cluster init_standby(context, master_hostname, options, segment_hostname) @@ -3038,7 +3038,6 @@ def impl(context, command, target): @then('verify that a role "{role_name}" exists in database "{dbname}"') def impl(context, role_name, dbname): query = "select rolname from pg_roles where rolname = '%s'" % role_name - conn = dbconn.connect(dbconn.DbURL(dbname=dbname), unsetSearchPath=False) try: result = getRows(dbname, query)[0][0] if result != role_name: @@ -3482,12 +3481,12 @@ def impl(context, num_of_segments): if content > -1 and status == 'u': end_data_segments += 1 - if int(num_of_segments) == int(end_data_segments - context.start_data_segments): - return + if int(num_of_segments) == int(end_data_segments - context.start_data_segments): + return - raise Exception("Incorrect amount of segments.\nprevious: %s\ncurrent:" - "%s\ndump of gp_segment_configuration: %s" % - (context.start_data_segments, end_data_segments, rows)) + raise Exception("Incorrect amount of segments.\nprevious: %s\ncurrent:" + "%s\ndump of gp_segment_configuration: %s" % + (context.start_data_segments, end_data_segments, rows)) @when('verify that {table_name} catalog table is present on new segments') @then('verify that {table_name} catalog table is present on new segments') @@ -3626,11 +3625,11 @@ def step_impl(context, options): query = """select datadir, port from pg_catalog.gp_segment_configuration where role='m' and content <> -1;""" cursor = dbconn.execSQL(conn, query) - for i in range(cursor.rowcount): - datadir, port = cursor.fetchone() - if datadir not in context.stdout_message or \ - str(port) not in context.stdout_message: - raise Exception("gpstate -m output missing expected mirror info, datadir %s port %d" %(datadir, port)) + for i in range(cursor.rowcount): + datadir, port = cursor.fetchone() + if datadir not in context.stdout_message or \ + str(port) not in context.stdout_message: + raise Exception("gpstate -m output missing expected mirror info, datadir %s port %d" %(datadir, port)) else: raise Exception("no verification for gpstate option given") @@ -3740,12 +3739,11 @@ def impl(context): @then('verify the dml results again in a new transaction') def impl(context): dbname = 'gptest' - conn = dbconn.connect(dbconn.DbURL(dbname=dbname), unsetSearchPath=False) - - for dml, job in context.dml_jobs: - code, message = job.reverify(conn) - if not code: - raise Exception(message) + with dbconn.connect(dbconn.DbURL(dbname=dbname), unsetSearchPath=False) as conn: + for dml, job in context.dml_jobs: + code, message = job.reverify(conn) + if not code: + raise Exception(message) @@ -4062,8 +4060,8 @@ def impl(context): gp_segment_configuration_backup = 'gpexpand.gp_segment_configuration' query = "select hostname, datadir from gp_segment_configuration where content = -1 order by dbid" - conn = dbconn.connect(dbconn.DbURL(dbname='postgres'), unsetSearchPath=False) - res = dbconn.execSQL(conn, query).fetchall() + with dbconn.connect(dbconn.DbURL(dbname='postgres'), unsetSearchPath=False) as conn: + res = dbconn.execSQL(conn, query).fetchall() master = res[0] standby = res[1] diff --git a/gpMgmt/test/behave/mgmt_utils/steps/replication_slots_utils.py b/gpMgmt/test/behave/mgmt_utils/steps/replication_slots_utils.py index 7e00679071a3..fe284386a222 100644 --- a/gpMgmt/test/behave/mgmt_utils/steps/replication_slots_utils.py +++ b/gpMgmt/test/behave/mgmt_utils/steps/replication_slots_utils.py @@ -7,6 +7,7 @@ run_command, stop_primary, execute_sql, + query_sql, wait_for_unblocked_transactions, ) @@ -65,12 +66,12 @@ def expand(context): def ensure_primary_mirror_switched_roles(): - results = execute_sql( + results = query_sql( "postgres", "select * from gp_segment_configuration where preferred_role <> role" ) - if results.rowcount != 2: + if len(results) != 2: raise Exception("expected 2 segments to not be in preferred roles") @@ -82,9 +83,9 @@ def step_impl(context): @given(u'a mirror has crashed') @when(u'a mirror has crashed') def step_impl(context): - host, datadir = execute_sql("postgres", + host, datadir = query_sql("postgres", "SELECT hostname, datadir FROM gp_segment_configuration WHERE role='m' AND content=0" - ).fetchone() + )[0] # NOTE that these commands are manually escaped; beware when adding dollar # signs or double-quotes! @@ -107,16 +108,16 @@ def step_impl(context): @then(u'the primaries and mirrors should be replicating using replication slots') def step_impl(context): - result_cursor = execute_sql( + results = query_sql( "postgres", "select pg_get_replication_slots() from gp_dist_random('gp_id') order by gp_segment_id" ) - if result_cursor.rowcount != context.current_cluster_size: + if len(results) != context.current_cluster_size: raise Exception("expected all %d primaries to have replication slots, only %d have slots" % (context.current_cluster_size, results.rowcount)) - for content_id, result in enumerate(result_cursor.fetchall()): - if not result[0].startswith('(internal_wal_replication_slot,,physical,,t,'): + for content_id, result in enumerate(results): + if result[0][:6] != ('internal_wal_replication_slot', None, 'physical', None, 't', None): raise Exception( "expected replication slot to be active for content id %d, got %s" % (content_id, result[0]) @@ -124,12 +125,12 @@ def step_impl(context): @then(u'the mirrors should not have replication slots') def step_impl(context): - result_cursor = execute_sql( + result_rows = query_sql( "postgres", "select datadir from gp_segment_configuration where role='m';" ) - for content_id, result in enumerate(result_cursor.fetchall()): + for content_id, result in enumerate(result_rows): path_to_replslot = os.path.join(result[0], 'pg_replslot') if len(os.listdir(path_to_replslot)) > 0: raise Exception("expected replication slot directory to be empty") diff --git a/gpMgmt/test/behave_utils/cluster_expand.py b/gpMgmt/test/behave_utils/cluster_expand.py index 5e1d5f6b09cf..8c9099b832a9 100755 --- a/gpMgmt/test/behave_utils/cluster_expand.py +++ b/gpMgmt/test/behave_utils/cluster_expand.py @@ -76,8 +76,8 @@ def initialize_segments(self, additional_params=''): def get_redistribute_status(self): sql = 'select status from gpexpand.status order by updated desc limit 1' dburl = dbconn.DbURL(dbname=self.database) - conn = dbconn.connect(dburl, encoding='UTF8', unsetSearchPath=False) - status = dbconn.execSQLForSingleton(conn, sql) + with dbconn.connect(dburl, encoding='UTF8', unsetSearchPath=False) as conn: + status = dbconn.execSQLForSingleton(conn, sql) if status == 'EXPANSION COMPLETE': rc = 0 else: diff --git a/gpMgmt/test/behave_utils/gpexpand_dml.py b/gpMgmt/test/behave_utils/gpexpand_dml.py index b8dafdd9b565..ce4801be3f14 100755 --- a/gpMgmt/test/behave_utils/gpexpand_dml.py +++ b/gpMgmt/test/behave_utils/gpexpand_dml.py @@ -29,12 +29,10 @@ def __init__(self, dbname, dmltype): self.prepare() def run(self): - conn = dbconn.connect(dbconn.DbURL(dbname=self.dbname), unsetSearchPath=False) - - self.loop(conn) - self.verify(conn) - - conn.commit() + with dbconn.connect(dbconn.DbURL(dbname=self.dbname), unsetSearchPath=False) as conn: + self.loop(conn) + self.verify(conn) + conn.commit() def prepare(self): sql = ''' @@ -46,12 +44,12 @@ def prepare(self): ) DISTRIBUTED BY (c1); '''.format(tablename=self.tablename) - conn = dbconn.connect(dbconn.DbURL(dbname=self.dbname), unsetSearchPath=False) - dbconn.execSQL(conn, sql) + with dbconn.connect(dbconn.DbURL(dbname=self.dbname), unsetSearchPath=False) as conn: + dbconn.execSQL(conn, sql) - self.prepare_extra(conn) + self.prepare_extra(conn) - conn.commit() + conn.commit() def prepare_extra(self, conn): pass @@ -85,7 +83,7 @@ def reverify(self, conn): return self.retval, self.retmsg - def verify(self): + def verify(self, conn): pass def stop(self): diff --git a/gpMgmt/test/behave_utils/utils.py b/gpMgmt/test/behave_utils/utils.py index fe416c70f451..6b7bfb7e4c99 100644 --- a/gpMgmt/test/behave_utils/utils.py +++ b/gpMgmt/test/behave_utils/utils.py @@ -29,10 +29,14 @@ def execute_sql(dbname, sql): - result = None + with dbconn.connect(dbconn.DbURL(dbname=dbname), unsetSearchPath=False) as conn: + dbconn.execSQL(conn, sql) + conn.commit() +def query_sql(dbname, sql): with dbconn.connect(dbconn.DbURL(dbname=dbname), unsetSearchPath=False) as conn: - result = dbconn.execSQL(conn, sql) + cursor = dbconn.execSQL(conn, sql) + result = cursor.fetchall() conn.commit() return result diff --git a/src/test/isolation2/expected/resgroup/resgroup_query_mem.out b/src/test/isolation2/expected/resgroup/resgroup_query_mem.out index 49e707f41f16..8e0b9193894e 100644 --- a/src/test/isolation2/expected/resgroup/resgroup_query_mem.out +++ b/src/test/isolation2/expected/resgroup/resgroup_query_mem.out @@ -25,9 +25,10 @@ select gp_inject_fault('rg_qmem_qd_qe', 'skip', dbid) from gp_segment_configurat (1 row) create function rg_qmem_test() returns boolean as $$ from pygresql.pg import DB from copy import deepcopy import re -# 1: get resgroup available mem in QD and QE and compute ratio sql = ("select memory_available m from " "gp_toolkit.gp_resgroup_status_per_segment " "where segment_id = %d and rsgname = 'admin_group'") qd_mem = int(plpy.execute(sql % -1)[0]["m"]) qe_mem = int(plpy.execute(sql % 0)[0]["m"]) ratio1 = int(round(float(qd_mem) / qe_mem)) -# 2. use notice to get qe operator mem dbname = plpy.execute("select current_database() db")[0]["db"] db = DB(dbname=dbname) db.query("set gp_resource_group_enable_recalculate_query_mem = on;") -sql = "select * from t_qmem order by 1" db.query(sql) qe_opmem_info = db.notices() qe_opmem = int(re.findall(r"op_mem=(\d+)", qe_opmem_info[0])[0]) +# 1: get resgroup available mem in QD and QE and compute ratio sql = ("select memory_available m from " "gp_toolkit.gp_resgroup_status_per_segment " "where segment_id = %d and rsgname = 'admin_group'") qd_mem = int(plpy.execute(sql % -1)[0]["m"]) qe_mem = int(plpy.execute(sql % 0)[0]["m"]) ratio1 = int(round(float(qd_mem) / qe_mem)) notices = [] +# 2. use notice to get qe operator mem dbname = plpy.execute("select current_database() db")[0]["db"] db = DB(dbname=dbname) +def notice_callback(notice): notices.append(notice.message) db.set_notice_receiver(notice_callback) db.query("set gp_resource_group_enable_recalculate_query_mem = on;") +sql = "select * from t_qmem order by 1" db.query(sql) qe_opmem_info = notices qe_opmem = int(re.findall(r"op_mem=(\d+)", qe_opmem_info[0])[0]) # 3. get qd operator mem sql = "explain analyze select * from pg_class order by relpages limit 10" db.query("set gp_resgroup_print_operator_memory_limits = on;") r = db.query(sql).getresult() for (line, ) in r: if "-> Sort" not in line: continue qd_opmem = int(re.findall(r"operatorMem: (\d+)", line)[0]) break db.query("set gp_resource_group_enable_recalculate_query_mem = off;") db.close() diff --git a/src/test/isolation2/expected/resgroup/resgroup_views.out b/src/test/isolation2/expected/resgroup/resgroup_views.out index 163363a1dfb2..8a3bc3613c91 100644 --- a/src/test/isolation2/expected/resgroup/resgroup_views.out +++ b/src/test/isolation2/expected/resgroup/resgroup_views.out @@ -7,7 +7,7 @@ select * from gp_toolkit.gp_resgroup_config where groupname='default_group'; select rsgname , groupid , num_running , num_queueing , num_queued , num_executed , cpu_usage->'-1' as qd_cpu_usage , memory_usage->'-1'->'used' as qd_memory_used , memory_usage->'-1'->'shared_used' as qd_memory_shared_used from gp_toolkit.gp_resgroup_status where rsgname='default_group'; rsgname | groupid | num_running | num_queueing | num_queued | num_executed | qd_cpu_usage | qd_memory_used | qd_memory_shared_used ---------------+---------+-------------+--------------+------------+--------------+--------------+----------------+----------------------- - default_group | 6437 | 0 | 0 | 0 | 0 | 0.00 | 0 | 0 + default_group | 6437 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 (1 row) select rsgname , groupid , cpu , memory_used , memory_shared_used from gp_toolkit.gp_resgroup_status_per_host s join gp_segment_configuration c on s.hostname=c.hostname and c.content=-1 and role='p' where rsgname='default_group'; diff --git a/src/test/isolation2/sql/resgroup/resgroup_query_mem.sql b/src/test/isolation2/sql/resgroup/resgroup_query_mem.sql index 7a0fbdfe88aa..73fee04b1946 100644 --- a/src/test/isolation2/sql/resgroup/resgroup_query_mem.sql +++ b/src/test/isolation2/sql/resgroup/resgroup_query_mem.sql @@ -29,15 +29,20 @@ sql = ("select memory_available m from " qd_mem = int(plpy.execute(sql % -1)[0]["m"]) qe_mem = int(plpy.execute(sql % 0)[0]["m"]) ratio1 = int(round(float(qd_mem) / qe_mem)) +notices = [] # 2. use notice to get qe operator mem dbname = plpy.execute("select current_database() db")[0]["db"] db = DB(dbname=dbname) + +def notice_callback(notice): + notices.append(notice.message) +db.set_notice_receiver(notice_callback) db.query("set gp_resource_group_enable_recalculate_query_mem = on;") sql = "select * from t_qmem order by 1" db.query(sql) -qe_opmem_info = db.notices() +qe_opmem_info = notices qe_opmem = int(re.findall(r"op_mem=(\d+)", qe_opmem_info[0])[0]) # 3. get qd operator mem diff --git a/src/test/isolation2/sql_isolation_testcase.py b/src/test/isolation2/sql_isolation_testcase.py index 95134c1ff0f1..2b9a85e3f17f 100755 --- a/src/test/isolation2/sql_isolation_testcase.py +++ b/src/test/isolation2/sql_isolation_testcase.py @@ -432,6 +432,11 @@ def connectdb(self, given_dbname, given_host = None, given_port = None, given_op time.sleep(0.1) else: raise + + def notice_callback(msg): + pass + if con: + con.set_notice_receiver(notice_callback) return con def get_hostname_port(self, contentid, role): @@ -473,7 +478,10 @@ def printout_result(self, r): for col in row: if col is None: col = "" - widths[colno] = max(widths[colno], len(str(col))) + width = len(str(col)) + if type(col) is bool: + width = 1 + widths[colno] = max(widths[colno], width) colno = colno + 1 # Start printing. Header first. @@ -503,6 +511,14 @@ def printout_result(self, r): result += "|" if col is None: col = "" + if type(col) is bool: + col = 't' if col else 'f' + if type(col) is list: + for i, elem in enumerate(col): + if type(elem) is bool: + col[i] = elem = 't' if elem else 'f' + col = "{" + ",".join([str(elem) for elem in col]) + "}" + result += " " + str(col).ljust(widths[colno]) + " " colno = colno + 1 result += "\n" @@ -524,7 +540,7 @@ def execute_command(self, command): if r and type(r) == str: echo_content = command[:-1].partition(" ")[0].upper() return "%s %s" % (echo_content, r) - elif r: + elif r is not None: return self.printout_result(r) else: echo_content = command[:-1].partition(" ")[0].upper() From 5c203862aaa066bac41653e0fae854336e6ff32b Mon Sep 17 00:00:00 2001 From: Viktor Kurilko Date: Wed, 28 Jan 2026 17:19:33 +0700 Subject: [PATCH 097/111] Apply stage 1 of futurize (#198) This patch is a result of using futurize -w -n --stage1 {python file} on the python code base. --- gpAux/gpdemo/gpsegwalrep.py | 31 +-- gpAux/gpperfmon/src/gpmon/gpmon_catqrynow.py | 3 +- gpAux/gpperfmon/src/gpmon/gpperfmon_install | 5 +- gpMgmt/bin/analyzedb | 12 +- .../el8_migrate_locale/el8_migrate_locale.py | 45 ++-- gpMgmt/bin/gpactivatestandby | 6 +- gpMgmt/bin/gpcheckcat | 86 +++---- .../gpcheckcat_modules/foreign_key_check.py | 2 +- .../repair_missing_extraneous.py | 2 +- gpMgmt/bin/gpcheckperf | 210 +++++++++--------- gpMgmt/bin/gpcheckresgroupimpl | 3 +- gpMgmt/bin/gpconfig | 21 +- .../gpconfig_modules/compare_segment_guc.py | 8 +- .../gpconfig_modules/parse_guc_metadata.py | 3 +- gpMgmt/bin/gpdeletesystem | 8 +- gpMgmt/bin/gpexpand | 73 +++--- gpMgmt/bin/gpinitstandby | 26 +-- gpMgmt/bin/gpload.py | 61 ++--- gpMgmt/bin/gpload_test/gpload/TEST.py | 5 +- gpMgmt/bin/gpload_test/gpload2/TEST.py | 13 +- gpMgmt/bin/gpload_test/gpload2/TEST_REMOTE.py | 11 +- .../gpload_test/gpload2/TEST_local_base.py | 3 +- .../gpload_test/gpload2/TEST_local_config.py | 7 +- .../gpload2/TEST_local_data_format.py | 7 +- .../gpload2/TEST_local_input_column.py | 7 +- .../gpload_test/gpload2/TEST_local_legacy.py | 7 +- .../gpload_test/gpload2/TEST_local_options.py | 7 +- .../gpload_test/gpload2/TEST_local_preload.py | 7 +- .../gpload2/TEST_local_schema_and_mode.py | 5 +- .../bin/gpload_test/gpload2/TEST_local_sql.py | 7 +- gpMgmt/bin/gpload_test/gpload2/conftest.py | 6 +- .../gpload2/data/transform/input_transform.py | 1 + gpMgmt/bin/gplogfilter | 21 +- gpMgmt/bin/gpmemreport | 45 ++-- gpMgmt/bin/gpmemwatcher | 41 ++-- gpMgmt/bin/gpmovemirrors | 10 +- gpMgmt/bin/gppylib/commands/base.py | 2 +- gpMgmt/bin/gppylib/commands/dca.py | 7 +- gpMgmt/bin/gppylib/commands/gp.py | 7 +- gpMgmt/bin/gppylib/commands/pg.py | 7 +- gpMgmt/bin/gppylib/commands/unix.py | 3 +- gpMgmt/bin/gppylib/datetimeutils.py | 26 +-- gpMgmt/bin/gppylib/db/catalog.py | 3 +- gpMgmt/bin/gppylib/db/dbconn.py | 13 +- gpMgmt/bin/gppylib/gparray.py | 10 +- gpMgmt/bin/gppylib/gpcatalog.py | 8 +- gpMgmt/bin/gppylib/gpparseopts.py | 9 +- gpMgmt/bin/gppylib/gpsubprocess.py | 4 +- gpMgmt/bin/gppylib/gpversion.py | 14 +- gpMgmt/bin/gppylib/logfilter.py | 45 ++-- gpMgmt/bin/gppylib/mainUtils.py | 10 +- gpMgmt/bin/gppylib/operations/__init__.py | 2 +- .../gppylib/operations/buildMirrorSegments.py | 5 +- gpMgmt/bin/gppylib/operations/initstandby.py | 4 +- gpMgmt/bin/gppylib/operations/package.py | 31 +-- .../gppylib/operations/rebalanceSegments.py | 2 +- .../test_regress_muck_with_internals.py | 12 +- ..._regress_muck_with_internals_on_standby.py | 8 +- .../gppylib/operations/test/test_package.py | 10 +- .../operations/test/unit/test_unit_utils.py | 10 +- gpMgmt/bin/gppylib/operations/unix.py | 2 +- gpMgmt/bin/gppylib/operations/utils.py | 8 +- gpMgmt/bin/gppylib/programs/clsAddMirrors.py | 7 +- .../bin/gppylib/programs/clsRecoverSegment.py | 2 +- gpMgmt/bin/gppylib/programs/clsSystemState.py | 5 +- gpMgmt/bin/gppylib/programs/gppkg.py | 6 +- .../gppylib/system/ComputeCatalogUpdate.py | 31 +-- gpMgmt/bin/gppylib/system/info.py | 5 +- gpMgmt/bin/gppylib/test/unit/gp_unittest.py | 1 + .../gppylib/test/unit/test_unit_command.py | 3 +- .../test/unit/test_unit_commands_unix.py | 3 +- .../unit/test_unit_compare_segment_guc.py | 3 +- .../unit/test_unit_database_segment_guc.py | 3 +- .../test/unit/test_unit_file_segment_guc.py | 3 +- .../test/unit/test_unit_foreign_key_check.py | 3 +- gpMgmt/bin/gppylib/test/unit/test_unit_gp.py | 3 +- .../bin/gppylib/test/unit/test_unit_gp_era.py | 3 +- .../test/unit/test_unit_gpaddmirrors.py | 3 +- .../gppylib/test/unit/test_unit_gparray.py | 3 +- .../gppylib/test/unit/test_unit_gpcheckcat.py | 3 +- .../unit/test_unit_gpcheckresgroupimpl.py | 104 ++++----- .../gppylib/test/unit/test_unit_gpconfig.py | 5 +- .../test/unit/test_unit_gpdeletesystem.py | 5 +- .../gppylib/test/unit/test_unit_gpexpand.py | 3 +- .../test/unit/test_unit_gpinitsystem.py | 3 +- .../bin/gppylib/test/unit/test_unit_gpload.py | 3 +- .../bin/gppylib/test/unit/test_unit_gppkg.py | 3 +- .../test/unit/test_unit_gprecoverseg.py | 3 +- .../bin/gppylib/test/unit/test_unit_gpssh.py | 3 +- .../gppylib/test/unit/test_unit_gpversion.py | 4 +- .../test/unit/test_unit_guccollection.py | 3 +- .../bin/gppylib/test/unit/test_unit_info.py | 3 +- .../unit/test_unit_leaked_schema_dropper.py | 3 +- .../gppylib/test/unit/test_unit_mainUtils.py | 3 +- .../gppylib/test/unit/test_unit_package.py | 3 +- .../test/unit/test_unit_parse_guc_metadata.py | 3 +- .../gppylib/test/unit/test_unit_parseutils.py | 10 +- .../test/unit/test_unit_rebalance_segment.py | 3 +- .../bin/gppylib/test/unit/test_unit_repair.py | 3 +- .../test_unit_repair_missing_extraneous.py | 3 +- .../test_unit_unique_index_violation_check.py | 3 +- gpMgmt/bin/gppylib/userinput.py | 11 +- gpMgmt/bin/gppylib/util/ssh_utils.py | 29 +-- .../util/test/unit/test_cluster_ssh_utils.py | 5 +- gpMgmt/bin/gppylib/utils.py | 15 +- gpMgmt/bin/gpscp | 17 +- gpMgmt/bin/gpsd | 13 +- gpMgmt/bin/gpssh | 47 ++-- gpMgmt/bin/gpssh-exkeys | 159 ++++++------- gpMgmt/bin/gpstart | 6 +- gpMgmt/bin/gpstop | 6 +- gpMgmt/bin/gpsys1 | 15 +- gpMgmt/bin/lib/gpconfigurenewsegment | 27 +-- gpMgmt/bin/lib/gppinggpfdist.py | 25 ++- gpMgmt/bin/lib/multidd | 1 + gpMgmt/bin/minirepro | 27 +-- gpMgmt/sbin/gpconfig_helper.py | 2 +- gpMgmt/sbin/gpoperation.py | 9 +- gpMgmt/sbin/packcore | 7 +- gpMgmt/sbin/recovery_base.py | 5 +- .../mgmt_utils/steps/gpconfig_mgmt_utils.py | 3 +- .../steps/gpssh_exkeys_mgmt_utils.py | 13 +- .../behave/mgmt_utils/steps/mgmt_utils.py | 17 +- .../mgmt_utils/steps/mirrors_mgmt_utils.py | 3 +- .../steps/replication_slots_utils.py | 3 +- gpMgmt/test/behave_utils/cluster_expand.py | 3 +- gpMgmt/test/behave_utils/cluster_setup.py | 11 +- gpMgmt/test/behave_utils/gpexpand_dml.py | 5 +- .../gpfdist_utils/gpfdist_mgmt.py | 2 +- gpMgmt/test/behave_utils/utils.py | 15 +- gpMgmt/test/parse_behave.py | 5 +- .../gp_replica_check/gp_replica_check.py | 35 +-- gpcontrib/gpcloud/bin/dummyHTTPServer.py | 23 +- .../gporca/concourse/build_and_test.py | 3 +- src/backend/gporca/scripts/cal_bitmap_test.py | 3 +- src/backend/gporca/scripts/fix_mdps.py | 25 ++- .../scripts/get_debug_event_counters.py | 3 +- src/include/catalog/li_extras.py | 3 +- src/test/isolation2/sql_isolation_testcase.py | 37 +-- src/test/locale/sort-test.py | 5 +- src/test/regress/mem_quota_util.py | 7 +- src/test/unit/mock/mocker.py | 5 +- src/tools/gdb/plan_dump.py | 1 + 143 files changed, 1058 insertions(+), 941 deletions(-) diff --git a/gpAux/gpdemo/gpsegwalrep.py b/gpAux/gpdemo/gpsegwalrep.py index 490f66b739f9..b718ec67f409 100755 --- a/gpAux/gpdemo/gpsegwalrep.py +++ b/gpAux/gpdemo/gpsegwalrep.py @@ -24,6 +24,7 @@ Assuming all of the above, you can just run the tool as so: ./gpsegwalrep.py [init|start|stop|destroy] """ +from __future__ import print_function import argparse import os @@ -49,22 +50,22 @@ def runcommands(commands, thread_name, command_finish, exit_on_error=True): output.append('%s: Running command... %s' % (datetime.datetime.now(), command)) with THREAD_LOCK: output = output + subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True).split('\n') - except subprocess.CalledProcessError, e: + except subprocess.CalledProcessError as e: output.append(str(e)) output.append(e.output) if exit_on_error: with PRINT_LOCK: for line in output: - print '%s: %s' % (thread_name, line) - print '' + print('%s: %s' % (thread_name, line)) + print('') sys.exit(e.returncode) output.append('%s: %s' % (datetime.datetime.now(), command_finish)) with PRINT_LOCK: for line in output: - print '%s: %s' % (thread_name, line) - print '' + print('%s: %s' % (thread_name, line)) + print('') def displaySegmentConfiguration(): commands = [] @@ -364,15 +365,15 @@ def refresh(self): query = ("SELECT dbid, content, port, datadir, role, preferred_role, status, mode " "FROM gp_segment_configuration s WHERE 1 = 1") - print '%s: fetching cluster configuration' % (datetime.datetime.now()) + print('%s: fetching cluster configuration' % (datetime.datetime.now())) dburl = dbconn.DbURL(self.hostname, self.port, self.dbname) - print '%s: fetched cluster configuration' % (datetime.datetime.now()) + print('%s: fetched cluster configuration' % (datetime.datetime.now())) try: with dbconn.connect(dburl, utility=True, unsetSearchPath=False) as conn: resultsets = dbconn.execSQL(conn, query).fetchall() - except Exception, e: - print e + except Exception as e: + print(e) sys.exit(1) contentIDs = Set() @@ -406,7 +407,7 @@ def refresh(self): contentIDs.add(seg_config.content) self.num_contents = len(contentIDs) - print 'found %d distinct content IDs' % (self.num_contents) + print('found %d distinct content IDs' % (self.num_contents)) def check_status_and_mode(self, expected_status, expected_mode): ''' Check if all the instance reached the expected_state and expected_mode ''' @@ -470,9 +471,9 @@ def WaitForRecover(cluster_configuration, max_retries = 200): number_of_segments = len(cluster_configuration.seg_configs) - print "cmd_all_sync: %s" % cmd_all_sync - print "cmd_find_error: %s" % cmd_find_error - print "number of contents: %s " % number_of_segments + print("cmd_all_sync: %s" % cmd_all_sync) + print("cmd_find_error: %s" % cmd_find_error) + print("number of contents: %s " % number_of_segments) retry_count = 1 while (retry_count < max_retries): @@ -487,7 +488,7 @@ def WaitForRecover(cluster_configuration, max_retries = 200): else: retry_count += 1 - print "WARNING: Incremental recovery took longer than expected!" + print("WARNING: Incremental recovery took longer than expected!") cmd_find_recovering = ("psql postgres -A -R ',' -t -c \"SELECT gp_segment_id" " FROM gp_stat_replication" " WHERE gp_segment_id in (%s) and sync_error = 'none'\"" % @@ -586,7 +587,7 @@ def ForceFTSProbeScan(cluster_configuration, expected_status = None, expected_mo StopInstances(cluster_config).run() elif args.operation == 'rebuild': if args.content is None: - print "ERROR: missing argument 'content' for rebuild operation" + print("ERROR: missing argument 'content' for rebuild operation") sys.exit(1) cluster_config_mirror = ClusterConfiguration(args.host, args.port, args.database, content=args.content, role=GpSegmentConfiguration.ROLE_MIRROR) diff --git a/gpAux/gpperfmon/src/gpmon/gpmon_catqrynow.py b/gpAux/gpperfmon/src/gpmon/gpmon_catqrynow.py index dc6f73da684a..890dd18a48a2 100644 --- a/gpAux/gpperfmon/src/gpmon/gpmon_catqrynow.py +++ b/gpAux/gpperfmon/src/gpmon/gpmon_catqrynow.py @@ -1,3 +1,4 @@ +from __future__ import print_function import os, sys, time GPMONDIR = 'gpperfmon/data' @@ -49,4 +50,4 @@ line[-3] = '"' + appname + '"' line[-2] = '"' + rsqname + '"' line[-1] = '"' + priority + '"' - print '|'.join(line).strip() + print('|'.join(line).strip()) diff --git a/gpAux/gpperfmon/src/gpmon/gpperfmon_install b/gpAux/gpperfmon/src/gpmon/gpperfmon_install index 0ae407345f06..9063798bb7a8 100755 --- a/gpAux/gpperfmon/src/gpmon/gpperfmon_install +++ b/gpAux/gpperfmon/src/gpmon/gpperfmon_install @@ -18,6 +18,7 @@ USAGE: gpperfmon_install --port GPDB_PORT [--enable --password GPMON_PASSWORD] --gpperfmonport sets the guc 'gpperfmon_port' for gpperfmon communication (default is 8888) --verbose will show output from sub-commands ''' +from __future__ import print_function import os, sys, time, re from subprocess import Popen @@ -28,7 +29,7 @@ try: from gppylib.userinput import ask_input from gppylib.gplog import get_default_logger, setup_tool_logging from gppylib.commands.unix import getLocalHostname, getUserName -except ImportError, e: +except ImportError as e: sys.exit('Cannot import modules. Please check that you have sourced greengage_path.sh. Detail: ' + str(e)) EXECNAME = os.path.split(__file__)[-1] @@ -79,7 +80,7 @@ def cli_help(): def usage(): - print cli_help() or __doc__ + print(cli_help() or __doc__) ###### main() diff --git a/gpMgmt/bin/analyzedb b/gpMgmt/bin/analyzedb index 0df9eb78cf21..2301a9a2521d 100755 --- a/gpMgmt/bin/analyzedb +++ b/gpMgmt/bin/analyzedb @@ -34,7 +34,7 @@ try: from gppylib.operations.unix import CheckDir, CheckFile, MakeDir from pygresql import pg -except ImportError, e: +except ImportError as e: sys.exit('Cannot import modules. Please check that you have sourced greengage_path.sh. Detail: ' + str(e)) EXECNAME = 'analyzedb' @@ -465,7 +465,7 @@ class AnalyzeDb(Operation): self._write_report(curr_ao_state, curr_last_op, heap_partitions, input_col_dict, root_partition_col_dict, dirty_partitions, target_list) logger.info("Done.") - except Exception, ex: + except Exception as ex: logger.exception(ex) raise @@ -968,7 +968,7 @@ def run_sql(conn, query): try: cursor = dbconn.execSQL(conn, query) res = cursor.fetchall() - except Exception, db_err: + except Exception as db_err: raise ExceptionNoStackTraceNeeded("%s" % db_err.__str__()) # .split('\n')[0]) cursor.close() return res @@ -1202,7 +1202,7 @@ def validate_dir(path): else: try: MakeDir(path).run() - except OSError, e: + except OSError as e: logger.exception("Could not create directory %s" % path) raise AnalyzeDirCreateFailed() else: @@ -1210,7 +1210,7 @@ def validate_dir(path): try: with tempfile.TemporaryFile(dir=path) as f: pass - except Exception, e: + except Exception as e: logger.exception("Cannot write to %s" % path) raise AnalyzeDirNotWritable() @@ -1432,7 +1432,7 @@ class AnalyzeWorker(Worker): self.pool.addFinishedWorkItem(self.cmd) self.cmd = None - except Exception, e: + except Exception as e: self.logger.exception(e) if self.cmd: self.logger.debug("[%s] finished cmd with exception: %s" % (self.name, self.cmd)) diff --git a/gpMgmt/bin/el8_migrate_locale/el8_migrate_locale.py b/gpMgmt/bin/el8_migrate_locale/el8_migrate_locale.py index 9eec9dabf8a2..dd0b5207baa1 100644 --- a/gpMgmt/bin/el8_migrate_locale/el8_migrate_locale.py +++ b/gpMgmt/bin/el8_migrate_locale/el8_migrate_locale.py @@ -1,5 +1,6 @@ #!/usr/bin/env python #!-*- coding: utf-8 -*- +from __future__ import print_function import argparse import sys from pygresql.pg import DB @@ -14,7 +15,7 @@ import re try: from pygresql import pg -except ImportError, e: +except ImportError as e: sys.exit('ERROR: Cannot import modules. Please check that you have sourced greengage_path.sh. Detail: ' + str(e)) class connection(object): @@ -40,7 +41,7 @@ def get_port_from_conf(self): file = datadir +'/postgresql.conf' if os.path.isfile(file): with open(file) as f: - for line in f.xreadlines(): + for line in f: match = re.search('port=\d+',line) if match: match1 = re.search('\d+', match.group()) @@ -113,21 +114,21 @@ def dump_index_info(self, fn): # print all catalog indexes that might be affected. cindex = self.get_affected_catalog_indexes() if cindex: - print>>f, "\c ", self.dbname + print("\c ", self.dbname, file=f) for indexrelid, indexname, tablename, collname, indexdef in cindex: - print>>f, "-- catalog indexrelid:", indexrelid, "| index name:", indexname, "| table name:", tablename, "| collname:", collname, "| indexdef: ", indexdef - print>>f, self.handle_one_index(indexname) - print>>f + print("-- catalog indexrelid:", indexrelid, "| index name:", indexname, "| table name:", tablename, "| collname:", collname, "| indexdef: ", indexdef, file=f) + print(self.handle_one_index(indexname), file=f) + print(file=f) # print all user indexes in all databases that might be affected. for dbname in dblist: index = self.get_affected_user_indexes(dbname) if index: - print>>f, "\c ", dbname + print("\c ", dbname, file=f) for indexrelid, indexname, tablename, collname, indexdef in index: - print>>f, "-- indexrelid:", indexrelid, "| index name:", indexname, "| table name:", tablename, "| collname:", collname, "| indexdef: ", indexdef - print>>f, self.handle_one_index(indexname) - print>>f + print("-- indexrelid:", indexrelid, "| index name:", indexname, "| table name:", tablename, "| collname:", collname, "| indexdef: ", indexdef, file=f) + print(self.handle_one_index(indexname), file=f) + print(file=f) f.close() @@ -215,7 +216,7 @@ def get_affected_partitioned_tables(self, dbname): result = tabsForDebug.getresult() logger.debug("There are {} range partitioning tables in database {}.".format(len(result), dbname)) if len(result): - print tabsForDebug + print(tabsForDebug) # Filtered partition range table that partition key in collate types. filterTabs = db.query(sql) @@ -223,7 +224,7 @@ def get_affected_partitioned_tables(self, dbname): if len(filterResult): logger.warning("There are {} range partitioning tables with partition key in collate types(like varchar, char, text) in database {}, these tables might be affected due to Glibc upgrade and should be checked when doing OS upgrade from EL7 to EL8.".format(len(filterResult), dbname)) if self.loglevel == logging.DEBUG: - print filterTabs + print(filterTabs) db.close() return filterResult @@ -238,7 +239,7 @@ def get_custom_opclass_as_distribute_keys_tables(self, dbname): result = tables.getresult() if result: logger.warning("There are {} tables in database {} that the distribution key is using custom operator class, should be checked when doing OS upgrade from EL7 to EL8.".format(len(result), dbname)) - print tables + print(tables) db.close() # Escape double-quotes in a string, so that the resulting string is suitable for @@ -319,9 +320,9 @@ def dump_tables(self, fn): # dump the table info to the specified output file if table_info: - print>>f, "-- order table by size in %s order " % 'ascending' if self.order_size_ascend else '-- order table by size in descending order' - print>>f, "\c ", dbname - print>>f + print("-- order table by size in %s order " % 'ascending' if self.order_size_ascend else '-- order table by size in descending order', file=f) + print("\c ", dbname, file=f) + print(file=f) # sort the tables by size if self.order_size_ascend: @@ -335,9 +336,9 @@ def dump_tables(self, fn): coll = result[2] attname = result[3] msg = result[4] - print>>f, "-- parrelid:", parrelid, "| coll:", coll, "| attname:", attname, "| msg:", msg - print>>f, self.handle_one_table(name) - print>>f + print("-- parrelid:", parrelid, "| coll:", coll, "| attname:", attname, "| msg:", msg, file=f) + print(self.handle_one_table(name), file=f) + print(file=f) # print the total partition table size self.print_size_summary_info() @@ -345,7 +346,7 @@ def dump_tables(self, fn): f.close() def print_size_summary_info(self): - print "---------------------------------------------" + print("---------------------------------------------") KB = float(1024) MB = float(KB ** 2) GB = float(KB ** 3) @@ -360,7 +361,7 @@ def print_size_summary_info(self): print("total partition tables : {}".format(self.total_roots)) print("total leaf partitions : {}".format(self.total_leafs)) - print "---------------------------------------------" + print("---------------------------------------------") # start multiple threads to do the check def concurrent_check(self, dbname): @@ -496,7 +497,7 @@ def run_alter_command(self, db_name, command): db.query(analyze_sql) db.close() - except Exception, e: + except Exception as e: logger.error("{}".format(str(e))) def parseargs(): diff --git a/gpMgmt/bin/gpactivatestandby b/gpMgmt/bin/gpactivatestandby index edfd5bf36e27..94d80bfa3cf8 100755 --- a/gpMgmt/bin/gpactivatestandby +++ b/gpMgmt/bin/gpactivatestandby @@ -31,7 +31,7 @@ try: from gppylib.gplog import get_default_logger, setup_tool_logging, enable_verbose_logging, get_logger_if_verbose from gppylib import gparray from gppylib.userinput import ask_yesno -except ImportError, e_: +except ImportError as e_: sys.exit('ERROR: Cannot import modules. Please check that you ' 'have sourced greengage_path.sh. Detail: ' + str(e_)) @@ -331,7 +331,7 @@ def promote_standby(master_data_dir): dbconn.execSQL(conn, 'CHECKPOINT') logger.info('Standby master is promoted') return True - except pygresql.InternalError, e: + except pygresql.InternalError as e: pass time.sleep(1) @@ -393,7 +393,7 @@ try: else: sys.exit(0) -except Exception, e_: +except Exception as e_: logger.fatal('Error activating standby master: %s' % str(e_)) sys.exit(2) diff --git a/gpMgmt/bin/gpcheckcat b/gpMgmt/bin/gpcheckcat index e57908cd68d4..ccf06e714528 100755 --- a/gpMgmt/bin/gpcheckcat +++ b/gpMgmt/bin/gpcheckcat @@ -21,6 +21,7 @@ Usage: gpcheckcat [