First of all, you can have a much simpler expression on the generate_series() table. Equivalent to yours (with the exception of the descending order, which in any case contradicts the rest of your question):
SELECT generate_series('2012-01-01'::date, now()::date, '1d')::date
The date type is forced to timestamptz automatically at the input. The return type is timestamptz anyway. I use the subquery below, so I can send it to date right away.
Next, max() , since the window function returns exactly what you need: the highest value, since the beginning of the frame ignores NULL values. Based on this, you get a radically simple request.
For this widget_id
Most likely faster than with CROSS JOIN or WITH RECURSIVE :
SELECT a.day, s.* FROM ( SELECT d.day ,max(s.for_date) OVER (ORDER BY d.day) AS effective_date FROM ( SELECT generate_series('2012-01-01'::date, now()::date, '1d')::date ) d(day) LEFT JOIN score s ON s.for_date = d.day AND s.widget_id = 1337
→ sqlfiddle
With this query, you can put any column from score in the final SELECT list. Put s. * For simplicity. Select columns.
If you want to start your withdrawal from the first day that actually has an account, simply replace the last LEFT JOIN with JOIN .
General form for everyone
widget_id>
Here I use CROSS JOIN to create a row for each widget on each date.
SELECT a.day, a.widget_id, s.score FROM ( SELECT d.day, w.widget_id ,max(s.for_date) OVER (PARTITION BY w.widget_id ORDER BY d.day) AS effective_date FROM (SELECT generate_series('2012-05-05'::date ,'2012-05-15'::date, '1d')::date AS day) d CROSS JOIN (SELECT DISTINCT widget_id FROM score) AS w LEFT JOIN score s ON s.for_date = d.day AND s.widget_id = w.widget_id ) a JOIN score s ON s.for_date = a.effective_date AND s.widget_id = a.widget_id
→ sqlfiddle