title: Liquibase tableExists not scaling
body: <p>I am running a ChangeSet that needs to check it a table exists in the precondition. So my ChangeSet looks like</p>
  
  <pre><code>&lt;changeSet author="James" id="1548255530845-100"&gt;
      &lt;preConditions onFail="MARK_RAN"&gt;
              &lt;not&gt;
                      &lt;tableExists tableName="MY_NEW_TABLE" schemaName="${my_schema}"&gt;&lt;/tableExists&gt;
              &lt;/not&gt;
      &lt;/preConditions&gt;
      &lt;createTable tableName="MY_NEW_TABLE"&gt;
          &lt;column name="IDX" type="${integer.type}"&gt;
              &lt;constraints nullable="false"/&gt;
          &lt;/column&gt;
          &lt;column name="INTVAL" type="${integer.type}"/&gt;
      &lt;/createTable&gt;
  &lt;/changeSet&gt;
  </code></pre>
  
  <p>When I run this on a large database/schema it is taking a very long time to run. </p>
  
  <p>Debugging the code the internals of Liquibase is calling SnapshotGeneratorFactory.has(DatabaseObject, Database)</p>
  
  <pre><code>public boolean has(DatabaseObject example, Database database) throws DatabaseException, InvalidExampleException {
      List&lt;Class&lt;? extends DatabaseObject&gt;&gt; types = new ArrayList&lt;Class&lt;? extends DatabaseObject&gt;&gt;(getContainerTypes(example.getClass(), database));
      types.add(example.getClass());
  
      //workaround for common check for databasechangelog/lock table to not snapshot the whole database like we have to in order to handle case issues
      if (example instanceof Table &amp;&amp; (example.getName().equals(database.getDatabaseChangeLogTableName()) || example.getName().equals(database.getDatabaseChangeLogLockTableName()))) {
          try {
              ExecutorService.getInstance().getExecutor(database).queryForInt(new RawSqlStatement("select count(*) from " + database.escapeObjectName(database.getLiquibaseCatalogName(), database.getLiquibaseSchemaName(), example.getName(), Table.class)));
              return true;
          } catch (DatabaseException e) {
              if (database instanceof PostgresDatabase) { //throws "current transaction is aborted" unless we roll back the connection
                  database.rollback();
              }
              return false;
          }
      }
  
      if (createSnapshot(example, database, new SnapshotControl(database, false, types.toArray(new Class[types.size()]))) != null) {
          return true;
      }
      CatalogAndSchema catalogAndSchema;
      if (example.getSchema() == null) {
          catalogAndSchema = database.getDefaultSchema();
      } else {
          catalogAndSchema = example.getSchema().toCatalogAndSchema();
      }
      DatabaseSnapshot snapshot = createSnapshot(catalogAndSchema, database, new SnapshotControl(database, false, example.getClass()));
      for (DatabaseObject obj : snapshot.get(example.getClass())) {
          if (DatabaseObjectComparatorFactory.getInstance().isSameObject(example, obj, null, database)) {
              return true;
          }
      }
      return false;
  }
  </code></pre>
  
  <p>The code is then creating a snapshot of the database &amp; looping through every table to check if it "isSameObject"</p>
  
  <p>This just does not scale, the more tables I have in my database the longer it is going to take to check if the table exists. </p>
  
  <p>I notice there is a comment in the method higher up for a "workaround" so looking up the databasechangelog/lock table does not have to perform this kind of snapshot then unscaleable look up. </p>
  
  <p>Is there be a scaleable way to check if a table exists/does not exist in a precondition? </p>
  
  <p>Another minor issue with the method: the List types at the start of the method contains "class liquibase.structure.core.Table" twice when I am checking if a tables exists. Shouldn't this be a Set - so avoiding the duplicate? And shouldn't it be only populated after the "workround" has been performed?</p>