33#include " storage/postgres_transaction.hpp"
44#include " storage/postgres_optimizer.hpp"
55#include " duckdb/planner/operator/logical_get.hpp"
6+ #include " duckdb/planner/operator/logical_limit.hpp"
67#include " storage/postgres_catalog.hpp"
78#include " postgres_scanner.hpp"
89
10+
911namespace duckdb {
1012
1113struct PostgresOperators {
1214 reference_map_t <PostgresCatalog, vector<reference<LogicalGet>>> scans;
1315};
1416
17+ static void OptimizePostgresScanLimitPushdown (unique_ptr<LogicalOperator> &op) {
18+ if (op->type == LogicalOperatorType::LOGICAL_LIMIT) {
19+ auto &limit = op->Cast <LogicalLimit>();
20+ reference<LogicalOperator> child = *op->children [0 ];
21+
22+ while (child.get ().type == LogicalOperatorType::LOGICAL_PROJECTION) {
23+ child = *child.get ().children [0 ];
24+ }
25+
26+ if (child.get ().type != LogicalOperatorType::LOGICAL_GET) {
27+ OptimizePostgresScanLimitPushdown (op->children [0 ]);
28+ return ;
29+ }
30+
31+ auto &get = child.get ().Cast <LogicalGet>();
32+ if (!PostgresCatalog::IsPostgresScan (get.function .name )) {
33+ OptimizePostgresScanLimitPushdown (op->children [0 ]);
34+ return ;
35+ }
36+
37+ switch (limit.limit_val .Type ()) {
38+ case LimitNodeType::CONSTANT_VALUE:
39+ case LimitNodeType::UNSET:
40+ break ;
41+ default :
42+ // not a constant or unset limit
43+ OptimizePostgresScanLimitPushdown (op->children [0 ]);
44+ return ;
45+ }
46+ switch (limit.offset_val .Type ()) {
47+ case LimitNodeType::CONSTANT_VALUE:
48+ case LimitNodeType::UNSET:
49+ break ;
50+ default :
51+ // not a constant or unset offset
52+ OptimizePostgresScanLimitPushdown (op->children [0 ]);
53+ return ;
54+ }
55+
56+ auto &bind_data = get.bind_data ->Cast <PostgresBindData>();
57+
58+ string generated_limit_clause = " " ;
59+ if (limit.limit_val .Type () != LimitNodeType::UNSET) {
60+ generated_limit_clause += " LIMIT " + to_string (limit.limit_val .GetConstantValue ());
61+ }
62+ if (limit.offset_val .Type () != LimitNodeType::UNSET) {
63+ generated_limit_clause += " OFFSET " + to_string (limit.offset_val .GetConstantValue ());
64+ }
65+
66+ if (!generated_limit_clause.empty ()) {
67+ bind_data.limit = generated_limit_clause;
68+
69+ op = std::move (op->children [0 ]);
70+ return ;
71+ }
72+ }
73+
74+ for (auto &child : op->children ) {
75+ OptimizePostgresScanLimitPushdown (child);
76+ }
77+ }
78+
1579void GatherPostgresScans (LogicalOperator &op, PostgresOperators &result) {
1680 if (op.type == LogicalOperatorType::LOGICAL_GET) {
1781 auto &get = op.Cast <LogicalGet>();
@@ -35,6 +99,8 @@ void GatherPostgresScans(LogicalOperator &op, PostgresOperators &result) {
3599}
36100
37101void PostgresOptimizer::Optimize (OptimizerExtensionInput &input, unique_ptr<LogicalOperator> &plan) {
102+ // look at query plan and check if we can find LIMIT/OFFSET to pushdown
103+ OptimizePostgresScanLimitPushdown (plan);
38104 // look at the query plan and check if we can enable streaming query scans
39105 PostgresOperators operators;
40106 GatherPostgresScans (*plan, operators);
0 commit comments