<!-- doc/src/sgml/limits.sgml -->

<appendix id="limits">
 <title><productname>PostgreSQL</productname> Limits</title>

 <para>
  <xref linkend="limits-table"/> describes various hard limits of
  <productname>PostgreSQL</productname>.  However, practical limits, such as
  performance limitations or available disk space may apply before absolute
  hard limits are reached.
 </para>

 <table id="limits-table">
  <title><productname>PostgreSQL</productname> Limitations</title>
  <tgroup cols="3">
   <thead>
    <row>
     <entry>Item</entry>
     <entry>Upper Limit</entry>
     <entry>Comment</entry>
    </row>
   </thead>

   <tbody>
    <row>
     <entry>database size</entry>
     <entry>unlimited</entry>
     <entry></entry>
    </row>

    <row>
     <entry>number of databases</entry>
     <!-- 2^32 - FirstNormalObjectId - 1 -->
     <entry>4,294,950,911</entry>
     <entry></entry>
    </row>

    <row>
     <entry>relations per database</entry>
     <!-- (2^32 - FirstNormalObjectId - 1) / 3 (3 because of the table and the
          two types that are created to go with it) -->
     <entry>1,431,650,303</entry>
     <entry></entry>
    </row>

    <row>
     <entry>relation size</entry>
     <entry>32 TB</entry>
     <entry>with the default <symbol>BLCKSZ</symbol> of 8192 bytes</entry>
    </row>

    <row>
     <entry>rows per table</entry>
     <entry>limited by the number of tuples that can fit onto 4,294,967,295 pages</entry>
     <entry></entry>
    </row>

    <row>
     <entry>columns per table</entry>
     <entry>1,600</entry>
     <entry>further limited by tuple size fitting on a single page; see note
     below</entry>
    </row>

    <row>
     <entry>columns in a result set</entry>
     <entry>1,664</entry>
     <entry></entry>
    </row>

    <row>
     <entry>field size</entry>
     <entry>1 GB</entry>
     <entry></entry>
    </row>

    <row>
     <entry>indexes per table</entry>
     <entry>unlimited</entry>
     <entry>constrained by maximum relations per database</entry>
    </row>

    <row>
     <entry>columns per index</entry>
     <entry>32</entry>
     <entry>can be increased by recompiling <productname>PostgreSQL</productname></entry>
    </row>

    <row>
     <entry>partition keys</entry>
     <entry>32</entry>
     <entry>can be increased by recompiling <productname>PostgreSQL</productname></entry>
    </row>

    <row>
     <entry>identifier length</entry>
     <entry>63 bytes</entry>
     <entry>can be increased by recompiling <productname>PostgreSQL</productname></entry>
    </row>

    <row>
     <entry>function arguments</entry>
     <entry>100</entry>
     <entry>can be increased by recompiling <productname>PostgreSQL</productname></entry>
    </row>

    <row>
     <entry>query parameters</entry>
     <entry>65,535</entry>
     <entry></entry>
    </row>
   </tbody>
  </tgroup>
 </table>

 <para>
  The maximum number of columns for a table is further reduced as the tuple
  being stored must fit in a single 8192-byte heap page.  For example,
  excluding the tuple header, a tuple made up of 1,600 <type>int</type> columns
  would consume 6400 bytes and could be stored in a heap page, but a tuple of
  1,600 <type>bigint</type> columns would consume 12800 bytes and would
  therefore not fit inside a heap page.
  Variable-length fields of
  types such as <type>text</type>, <type>varchar</type>, and <type>char</type>
  can have their values stored out of line in the table's TOAST table when the
  values are large enough to require it.  Only an 18-byte pointer must remain
  inside the tuple in the table's heap.  For shorter length variable-length
  fields, either a 4-byte or 1-byte field header is used and the value is
  stored inside the heap tuple.
 </para>

 <para>
  Columns that have been dropped from the table also contribute to the maximum
  column limit.  Moreover, although the dropped column values for newly
  created tuples are internally marked as null in the tuple's null bitmap, the
  null bitmap also occupies space.
 </para>

 <para>
  Each table can store a theoretical maximum of 2^32 out-of-line values; see
  <xref linkend="storage-toast" /> for a detailed discussion of out-of-line
  storage. This limit arises from the use of a 32-bit OID to identify each
  such value. The practical limit is significantly less than the theoretical
  limit, because as the OID space fills up, finding an OID that is still free
  can become expensive, in turn slowing down INSERT/UPDATE statements.
  Typically, this is only an issue for tables containing many terabytes
  of data; partitioning is a possible workaround.
 </para>
</appendix>
