Received: from maia.hub.org (maia-3.hub.org [200.46.204.243]) by mail.postgresql.org (Postfix) with ESMTP id 27B4DB5DC42 for ; Thu, 3 Nov 2011 14:48:11 -0300 (ADT) Received: from mail.postgresql.org ([200.46.204.86]) by maia.hub.org (mx1.hub.org [200.46.204.243]) (amavisd-maia, port 10024) with ESMTP id 16978-02 for ; Thu, 3 Nov 2011 17:48:03 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0-rc2 Received: from mail-vw0-f46.google.com (mail-vw0-f46.google.com [209.85.212.46]) by mail.postgresql.org (Postfix) with ESMTP id 30EC5B5DC6C for ; Thu, 3 Nov 2011 14:48:03 -0300 (ADT) Received: by mail-vw0-f46.google.com with SMTP id 1so1132314vws.19 for ; Thu, 03 Nov 2011 10:48:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=message-id:date:from:user-agent:mime-version:to:subject :content-type:content-transfer-encoding; bh=4+RsomQbtWs7DEqDsOa0Ms0j+r9DjLQ47Wc0dZCIpXI=; b=mKgFgHxq4pYethfIqK4yw7AJbJf72PNKyzg1lZ9vK5/wjMnQNRS9TX1CdzCO5mwd+h VFv/WhSWrG2AgQV1q1bGaneDdeIGf7J6kfCqCRX9hG3lxOfAdsZ4rPP3trr/jCwIs5iM 8CsA/7nGXJkWXNhx0zJ1pRkSfYhrLEKg8RGzE= Received: by 10.52.94.102 with SMTP id db6mr9227690vdb.118.1320342482165; Thu, 03 Nov 2011 10:48:02 -0700 (PDT) Received: from jay.local (c-24-63-250-125.hsd1.ma.comcast.net. [24.63.250.125]) by mx.google.com with ESMTPS id p7sm8799948vdh.9.2011.11.03.10.47.59 (version=SSLv3 cipher=OTHER); Thu, 03 Nov 2011 10:48:01 -0700 (PDT) Message-ID: <4EB2D3CB.7020708@gmail.com> Date: Thu, 03 Nov 2011 13:47:55 -0400 From: Jay Levitt User-Agent: Postbox 3.0.0 (Macintosh/20111031) MIME-Version: 1.0 To: pgsql-performance@postgresql.org Subject: Predicates not getting pushed into SQL function? Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-Virus-Scanned: Maia Mailguard 1.0.1 X-Spam-Status: No, hits=-1.899 tagged_above=-10 required=5 tests=BAYES_00=-1.9, FREEMAIL_FROM=0.001 X-Spam-Level: X-Archive-Number: 201111/60 X-Sequence-Number: 45571 I'm confused. I have a now-trivial SQL function that, unrestricted, would scan my whole users table. When I paste the body of the function as a subquery and restrict it to one row, it only produces one row. When I paste the body of the function into a view and restrict it to one row, it produces one row. But when I put it in a SQL function... it scans the whole users table and then throws the other rows away. I thought SQL functions were generally inline-able, push-down-able, etc. As a workaround, I can put my WHERE clause inside the function and pass it parameters, but that feels ugly, and it won't help for things like resticting via JOINs. The real function needs parameters, so I can't use it as a view. Are there better workarounds? I suspect the problem is (something like) the planner doesn't realize the function will produce a variable number of rows; I can specify COST or ROWS, but they're both fixed values. Pretty-printed function and explain analyze results: https://gist.github.com/1336963 In ASCII for web-haters and posterity: -- THE OVERLY SIMPLIFIED FUNCTION create or replace function matcher() returns table(user_id int, match int) as $$ select o.user_id, 1 as match from ( select u.id as user_id, u.gender from users as u ) as o cross join ( select u.id as user_id, u.gender from users as u where u.id = 1 ) as my; $$ language sql stable; -- WHEN I CALL IT AS A FUNCTION select * from matcher() where user_id = 2; LOG: duration: 1.242 ms plan: Query Text: select o.user_id, 1 as match from ( select u.id as user_id, u.gender from users as u ) as o cross join ( select u.id as user_id, u.gender from users as u where u.id = 1 ) as my; Nested Loop (cost=0.00..118.39 rows=1656 width=4) (actual time=0.022..0.888 rows=1613 loops=1) Output: u.id, 1 -> Index Scan using users_pkey on public.users u (cost=0.00..8.27 rows=1 width=0) (actual time=0.013..0.015 rows=1 loops=1) Index Cond: (u.id = 1) -> Seq Scan on public.users u (cost=0.00..93.56 rows=1656 width=4) (actual time=0.004..0.479 rows=1613 loops=1) Output: u.id CONTEXT: SQL function "matcher" statement 1 LOG: duration: 1.951 ms plan: Query Text: select * from matcher() where user_id = 2; Function Scan on public.matcher (cost=0.25..12.75 rows=5 width=8) (actual time=1.687..1.940 rows=1 loops=1) Output: user_id, match Filter: (matcher.user_id = 2) -- WHEN I CALL IT AS A SUBQUERY select * from ( select o.user_id, 1 as match from ( select u.id as user_id, u.gender from users as u ) as o cross join ( select u.id as user_id, u.gender from users as u where u.id = 1 ) as my ) as matcher where user_id = 2; LOG: duration: 0.044 ms plan: Query Text: select * from ( select o.user_id, 1 as match from ( select u.id as user_id, u.gender from users as u ) as o cross join ( select u.id as user_id, u.gender from users as u where u.id = 1 ) as my ) as matcher where user_id = 2; Nested Loop (cost=0.00..16.55 rows=1 width=4) (actual time=0.028..0.031 rows=1 loops=1) Output: u.id, 1 -> Index Scan using users_pkey on public.users u (cost=0.00..8.27 rows=1 width=4) (actual time=0.021..0.022 rows=1 loops=1) Output: u.id Index Cond: (u.id = 2) -> Index Scan using users_pkey on public.users u (cost=0.00..8.27 rows=1 width=0) (actual time=0.004..0.006 rows=1 loops=1) Index Cond: (u.id = 1) -- WHEN I CALL IT AS A VIEW create view matchview as select o.user_id, 1 as match from ( select u.id as user_id, u.gender from users as u ) as o cross join ( select u.id as user_id, u.gender from users as u where u.id = 1 ) as my; select * from matchview where user_id = 2; LOG: duration: 0.044 ms plan: Query Text: select * from matchview where user_id = 2; Nested Loop (cost=0.00..16.55 rows=1 width=4) (actual time=0.028..0.031 rows=1 loops=1) Output: u.id, 1 -> Index Scan using users_pkey on public.users u (cost=0.00..8.27 rows=1 width=4) (actual time=0.021..0.022 rows=1 loops=1) Output: u.id Index Cond: (u.id = 2) -> Index Scan using users_pkey on public.users u (cost=0.00..8.27 rows=1 width=0) (actual time=0.005..0.007 rows=1 loops=1) Index Cond: (u.id = 1)