In postgres, a cte should not be considered an inline view, although thinking of it as a materialized view that lives within a scope of a statement is useful. A CTE will be materialized if it is referenced in another part of the query, or if it is alters data (INSERT / UPDATE / DELETE).
So, since your example alters data, the CTE is evaluated, whereas the link you refer to has a CTE that doesn't alter data.
Whereas in other databases, predicates from the outer query will be pushed down to the CTE by the optimizer, in postgresql, the CTE will be fully materialized.
e.g.
WITH cte AS (SELECT * FROM foo WHERE foo.bar = True)
SELECT * FROM cte WHERE cte.id > 10 AND cte.id < 20
is slower than in postgresql
SELECT * FROM (SELECT * FROM foo WHERE bar = TRUE) cte
WHERE cte.id > 10 AND cte.id < 20
This is a consideration where one has optional or dynamic predicates in the outer query. The slightly faster CTE version would be
WITH cte AS (SELECT * FROM foo WHERE foo.bar = True AND foo.id > 10 AND foo.id < 20)
SELECT * FROM cte
This is by design, and you can rely on this behaviour to create an optimization barrier.
CTEs are allowed wherever a SELECT clause is allowed. So, it is possible to use CTEs inside of INSERT, UPDATE or DELETE statements. I'm not sure if this is part of the SQL standard.
For instance, until version 9.5 and the introduction of INSERT ... ON CONFLICT
syntax, we could use a CTE to perform an UPSERT. Here's a SO thread that illustrates with an example
There is a 2nd more interesting type of CTE, RECURSIVE CTE, where a CTE is composed of a union of an iterative part and an applicative part that can work on values generated in the iterative part. I don't think this type of query could be inlined anyway.