I found the reason for the row chaining within a data block. We calculate the size of the undo record needed to update the row piece, and if it is larger than the maximum size of an undo record, we reduce the number of columns in the row piece until it fits. There is quite a bit of overhead in an undo record, and for some parts it is multiplied by the number of columns. A row with 194 columns with a total column size of 394 bytes would require 2520 bytes of undo. In a 2K block we can store only 2024 bytes of undo.