* @param schemaPattern String
	 * @param typeNamePattern String
	 * @param types int[]
	 * @throws SQLException
	 * @return ResultSet
	 */
	public java.sql.ResultSet getUDTs(String catalog, String schemaPattern,
		String typeNamePattern, int[] types) throws SQLException
	{
		String sql = "select "
			+ "null as type_cat, n.nspname as type_schem, t.typname as type_name,  null as class_name, "
			+ "CASE WHEN t.typtype='c' then "
			+ java.sql.Types.STRUCT
			+ " else "
			+ java.sql.Types.DISTINCT
			+ " end as data_type, pg_catalog.obj_description(t.oid, 'pg_type')  "
			+ "as remarks, CASE WHEN t.typtype = 'd' then  (select CASE";

		for(int i = 0; i < SPIConnection.JDBC3_TYPE_NAMES.length; i++)
		{
			sql += " when typname = '" + SPIConnection.JDBC_TYPE_NUMBERS[i]
				+ "' then " + SPIConnection.JDBC_TYPE_NUMBERS[i];
		}

		sql += " else "
			+ java.sql.Types.OTHER
			+ " end from pg_type where oid=t.typbasetype) "
			+ "else null end as base_type "
			+ "from pg_catalog.pg_type t, pg_catalog.pg_namespace n where t.typnamespace = n.oid and n.nspname != 'pg_catalog' and n.nspname != 'pg_toast'";

		String toAdd = "";
		if(types != null)
		{
			toAdd += " and (false ";
			for(int i = 0; i < types.length; i++)
			{
				switch(types[i])
				{
					case java.sql.Types.STRUCT:
						toAdd += " or t.typtype = 'c'";
						break;
					case java.sql.Types.DISTINCT:
						toAdd += " or t.typtype = 'd'";
						break;
				}
			}
			toAdd += " ) ";
		}
		else
		{
			toAdd += " and t.typtype IN ('c','d') ";
		}
		// spec says that if typeNamePattern is a fully qualified name
		// then the schema and catalog are ignored

		if(typeNamePattern != null)
		{
			// search for qualifier
			int firstQualifier = typeNamePattern.indexOf('.');
			int secondQualifier = typeNamePattern.lastIndexOf('.');

			if(firstQualifier != -1) // if one of them is -1 they both will
										// be
			{
				if(firstQualifier != secondQualifier)
				{
					// we have a catalog.schema.typename, ignore catalog
					schemaPattern = typeNamePattern.substring(
						firstQualifier + 1, secondQualifier);
				}
				else
				{
					// we just have a schema.typename
					schemaPattern = typeNamePattern
						.substring(0, firstQualifier);
				}
				// strip out just the typeName
				typeNamePattern = typeNamePattern
					.substring(secondQualifier + 1);
			}
			toAdd += " and t.typname like '" + escapeQuotes(typeNamePattern)
				+ "'";
		}

		// schemaPattern may have been modified above
		if(schemaPattern != null)
		{
			toAdd += " and n.nspname like '" + escapeQuotes(schemaPattern)
				+ "'";
		}
		sql += toAdd;
		sql += " order by data_type, type_schem, type_name";
		java.sql.ResultSet rs = createMetaDataStatement().executeQuery(sql);

		return rs;
	}