In PostgreSQL (at least starting at 9.4), CTEs act as optimization barriers .
The query optimizer will not smooth CTE terms into an external query, discard qualifiers, or pull qualifiers even in trivial cases. Thus, an unqualified SELECT inside the term CTE will always perform a full table scan (or scan only by index, if there is a suitable index).
So in PostgreSQL, these two things are very different, since a simple EXPLAIN will show:
with parent as ( select * from a101 ) select * from parent where value1 = 159
and
SELECT * FROM ( SELECT * FROM a101 ) AS parent WHERE value1 = 159;
However, that “scanning the entire table” does not necessarily mean “will load the entire table into memory”. PostgreSQL will use TupleStore, which will transparently spill onto a temporary file on disk as it grows.
The initial rationale was that DML in terms of CTE was planned (and later implemented). If DML in terms of CTE is vital, its execution is predictable and complete. This may also be true if the CTE calls data modification functions.
Unfortunately, no one seemed to think "... but what if it's just SELECT and we want to embed it?".
Many in the community seem to see this as a function and regularly make it public as a workaround for optimizer issues. I find this attitude completely perplexing. As a result, it will be very difficult to fix this later, because people intentionally use CTE when they want the optimizer not to modify the query.
In other words, PostgreSQL abuses CTE as pseudo-query-hints (along with the OFFSET 0 hack), because the project policy says that hints of real queries are undesirable or not supported.
AFAIK MS SQL Server can optimize CTE barriers, but can also choose to materialize a result set.