View Javadoc
1   package org.dbunit.ext.postgresql;
2   
3   import static org.assertj.core.api.Assertions.assertThat;
4   
5   import java.io.StringReader;
6   import java.sql.Statement;
7   import java.sql.Types;
8   import java.util.Objects;
9   
10  import org.dbunit.DatabaseEnvironment;
11  import org.dbunit.database.DatabaseConfig;
12  import org.dbunit.database.IDatabaseConnection;
13  import org.dbunit.dataset.Column;
14  import org.dbunit.dataset.IDataSet;
15  import org.dbunit.dataset.ITable;
16  import org.dbunit.dataset.ITableMetaData;
17  import org.dbunit.dataset.ReplacementDataSet;
18  import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
19  import org.dbunit.operation.DatabaseOperation;
20  import org.junit.jupiter.api.AfterEach;
21  import org.junit.jupiter.api.BeforeEach;
22  import org.junit.jupiter.api.Test;
23  import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
24  import org.xml.sax.InputSource;
25  
26  @EnabledIfSystemProperty(named = "dbunit.profile", matches = "postgresql")
27  class PostgresSQLOidIT
28  {
29      private IDatabaseConnection _connection;
30      private final String testTable = "t2";
31      // @formatter:off
32      private static final String xmlData = "<?xml version=\"1.0\"?>" +
33              "<dataset>" +
34              "<T2 DATA=\"[NULL]\" />" +
35              "<T2 DATA=\"\\[text UTF-8](Anything)\" />" +
36              "</dataset>";
37      // @formatter:on
38  
39      @BeforeEach
40      protected void setUp() throws Exception
41      {
42          // Load active postgreSQL profile and connection from Maven pom.xml.
43          _connection = DatabaseEnvironment.getInstance().getConnection();
44          final Statement stat = _connection.getConnection().createStatement();
45          // DELETE SQL OID tables
46          stat.execute("DROP TABLE IF EXISTS " + testTable + ";");
47  
48          // Create SQL OID tables
49          stat.execute("CREATE TABLE " + testTable + "(DATA OID);");
50          stat.close();
51          // TODO found that if close the connection and create again the t2 table
52          // will be there for test. There must be something i'm missing on this.
53          _connection.close();
54          _connection = DatabaseEnvironment.getInstance().getConnection();
55      }
56  
57      @AfterEach
58      protected void tearDown() throws Exception
59      {
60          if (!Objects.isNull(_connection))
61          {
62              final Statement stat =
63                      _connection.getConnection().createStatement();
64              // DELETE SQL OID tables
65              stat.execute("DROP TABLE IF EXISTS " + testTable + ";");
66              _connection.close();
67  
68              _connection = null;
69          }
70      }
71  
72      @Test
73      void testOidDataType_withNullAndBinaryValues_roundTripsThroughDatabase() throws Exception
74      {
75          assertThat(_connection).as("didn't get a connection").isNotNull();
76          final DatabaseConfig config = _connection.getConfig();
77          config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
78                  new PostgresqlDataTypeFactory());
79  
80          final ReplacementDataSet dataSet =
81                  new ReplacementDataSet(new FlatXmlDataSetBuilder()
82                          .build(new InputSource(new StringReader(xmlData))));
83          dataSet.addReplacementObject("[NULL]", null);
84          dataSet.setStrictReplacement(true);
85  
86          IDataSet ids;
87          ids = _connection.createDataSet();
88          final ITableMetaData itmd = ids.getTableMetaData(testTable);
89          final Column[] cols = itmd.getColumns();
90          ids = _connection.createDataSet();
91          for (final Column col : cols)
92          {
93              assertThat(col.getDataType().getSqlType())
94                      .isEqualTo(Types.BIGINT);
95              assertThat(col.getSqlTypeName()).isEqualTo("oid");
96          }
97  
98          DatabaseOperation.CLEAN_INSERT.execute(_connection, dataSet);
99          ids = _connection.createDataSet();
100         final ITable it = ids.getTable(testTable);
101         assertThat(it.getValue(0, "DATA")).isNull();
102         assertThat("\\[text UTF-8](Anything)".getBytes())
103                 .isEqualTo(it.getValue(1, "DATA"));
104     }
105 
106     /**
107      * Issue 693: a PostgreSQL oid column is a generic object identifier, not necessarily a large
108      * object reference (see https://www.postgresql.org/docs/current/datatype-oid.html), e.g. a
109      * real catalog object's own oid such as a table's oid via <code>'table'::regclass</code>,
110      * the original bug report's own example. Reading such a row must not fail the whole read,
111      * and the connection must remain usable for subsequent rows afterward.
112      */
113     @Test
114     void testOidDataType_withOidNotReferencingALargeObject_readsAsNullInsteadOfFailing()
115             throws Exception
116     {
117         assertThat(_connection).as("didn't get a connection").isNotNull();
118         final DatabaseConfig config = _connection.getConfig();
119         config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
120                 new PostgresqlDataTypeFactory());
121 
122         // dbUnit's own write path (setSqlValue()) always creates a genuine large object, so use
123         // raw SQL - two distinct real catalog oids - to put non-large-object values into the
124         // column.
125         try (Statement stat = _connection.getConnection().createStatement())
126         {
127             stat.execute("INSERT INTO " + testTable + "(DATA) VALUES ('pg_class'::regclass::oid)");
128             stat.execute("INSERT INTO " + testTable + "(DATA) VALUES ('pg_proc'::regclass::oid)");
129         }
130 
131         final IDataSet ids = _connection.createDataSet();
132         final ITable it = ids.getTable(testTable);
133 
134         assertThat(it.getRowCount()).isEqualTo(2);
135         assertThat(it.getValue(0, "DATA"))
136                 .as("a non-large-object oid should read as null instead of throwing.")
137                 .isNull();
138         assertThat(it.getValue(1, "DATA"))
139                 .as("reading a second, distinct non-large-object oid afterward should still "
140                         + "work, proving the connection recovered from the first failed read.")
141                 .isNull();
142     }
143 }