I have a contract that calls another contract that calls another. If there is a throw (or out-of-gas error) that occurs somewhere in the second contract, does the entire transaction get reverted or is it just the stuff in the second and third contract that does, while the execution from the first contract still stands?
In Solidity, by default, yes.
On the EVM-level, a throw (bad jump, out-of-gas, or any other exception) only reverts the call it is inside. Solidity helpfully continues the exception down the stack until everything is undone.
It is possible, using lower-level code (specifically, address.call()), to prevent this. Here is an example of this being used as an improvised try-catch construct.
To complement @Matthew's answer, it depends on how the call is made in Solidity.
foo does a
throw, then yes the entire transaction is reverted.
C does a "lower-level raw call" like
foo does a
throw, then only
foo and its subcalls are reverted. This is because a raw call does not propagate any exceptions: a raw call like
D.call only returns a boolean which indicates if the call succeeded or encountered an exception.
In Solidity, a
throw causes an exception by generating bytecode that leads to an invalid jump destination. (For the other cases, see All cases when Solidity compiles to invalid jump destination.)
From the Solidity perspective, an exception that is not swallowed or is uncaught/unhandled, will cause the entire transaction to be reverted.
There is currently no way to catch an exception in Solidity, thus an exception, such as
throw, will cause the entire transaction to be reverted.
However, raw calls "swallow" exceptions: raw calls don't propagate exceptions and that's why only the subcalls will be reverted.