This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 3a6a62fb82161f7516804a62e98c361ff821f231
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Fri Aug 16 11:25:26 2024 +0200

    Use "LIKE" instead of strict equality for the authority part when searching 
for "authority:code" in the "SPATIAL_REF_SYS" table.
---
 .../sis/metadata/sql/privy/SQLUtilities.java       | 22 +++++++--
 .../referencing/factory/sql/EPSGDataAccess.java    | 14 +-----
 .../apache/sis/storage/sql/feature/Database.java   |  6 ---
 .../sis/storage/sql/feature/InfoStatements.java    | 53 ++++++++++++----------
 .../storage/sql/feature/InfoStatementsTest.java    | 13 +++---
 .../sis/storage/sql/postgis/PostgresTest.java      |  1 +
 6 files changed, 57 insertions(+), 52 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/SQLUtilities.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/SQLUtilities.java
index 474aa90b25..c3196ad286 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/SQLUtilities.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/privy/SQLUtilities.java
@@ -125,24 +125,38 @@ public final class SQLUtilities extends Static {
     }
 
     /**
-     * Returns a SQL LIKE pattern for the given identifier. The identifier is 
optionally returned in all lower cases
+     * Returns a string like the given string but with accented letters 
replaced by ASCII letters
+     * and all characters that are not letter or digit replaced by the 
wildcard % character.
+     *
+     * @param  text     the text to get as a SQL LIKE pattern.
+     * @param  toLower  whether to convert characters to lower case.
+     * @return the "LIKE" pattern for the given text.
+     */
+    public static String toLikePattern(final String text, final boolean 
toLower) {
+        final var buffer = new StringBuilder(text.length());
+        toLikePattern(text, 0, text.length(), false, toLower, buffer);
+        return buffer.toString();
+    }
+
+    /**
+     * Returns a SQL LIKE pattern for the given text. The text is optionally 
returned in all lower cases
      * for allowing case-insensitive searches. Punctuations are replaced by 
any sequence of characters ({@code '%'})
      * and non-ASCII letters or digits are replaced by any single character 
({@code '_'}). This method avoid to put
      * a {@code '%'} symbol as the first character since it prevents some 
databases to use their index.
      *
-     * @param  identifier   the identifier to get as a SQL LIKE pattern.
+     * @param  text         the text to get as a SQL LIKE pattern.
      * @param  i            index of the first character to use in the given 
{@code identifier}.
      * @param  end          index after the last character to use in the given 
{@code identifier}.
      * @param  allowSuffix  whether to append a final {@code '%'} wildcard at 
the end of the pattern.
      * @param  toLower      whether to convert characters to lower case.
      * @param  buffer       buffer where to append the SQL LIKE pattern.
      */
-    public static void toLikePattern(final String identifier, int i, final int 
end,
+    public static void toLikePattern(final String text, int i, final int end,
             final boolean allowSuffix, final boolean toLower, final 
StringBuilder buffer)
     {
         final int bs = buffer.length();
         while (i < end) {
-            final int c = identifier.codePointAt(i);
+            final int c = text.codePointAt(i);
             if (Character.isLetterOrDigit(c)) {
                 if (c < 128) {                      // Use only ASCII 
characters in the search.
                     buffer.appendCodePoint(toLower ? Character.toLowerCase(c) 
: c);
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
index 21cf4bec22..cdf84ef70a 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
@@ -694,7 +694,7 @@ codes:  for (int i=0; i<codes.length; i++) {
                  * We search first in the primary table. If no name is not 
found there, then we
                  * will search in the aliases table as a fallback.
                  */
-                final String pattern = toLikePattern(code);
+                final String pattern = SQLUtilities.toLikePattern(code, false);
                 Integer resolved = null;
                 boolean alias = false;
                 do {
@@ -1205,16 +1205,6 @@ codes:  for (int i=0; i<codes.length; i++) {
         return properties;
     }
 
-    /**
-     * Returns a string like the given string but with accented letters 
replaced by ASCII letters
-     * and all characters that are not letter or digit replaced by the 
wildcard % character.
-     */
-    private static String toLikePattern(final String name) {
-        final StringBuilder buffer = new StringBuilder(name.length());
-        SQLUtilities.toLikePattern(name, 0, name.length(), false, false, 
buffer);
-        return buffer.toString();
-    }
-
     /**
      * Returns an arbitrary object from a code. The default implementation 
delegates to more specific methods,
      * for example {@link #createCoordinateReferenceSystem(String)}, {@link 
#createDatum(String)}, <i>etc.</i>
@@ -1266,7 +1256,7 @@ codes:  for (int i=0; i<codes.length; i++) {
                     if (isPrimaryKey) {
                         stmt.setInt(1, pk);
                     } else {
-                        stmt.setString(1, toLikePattern(code));
+                        stmt.setString(1, SQLUtilities.toLikePattern(code, 
false));
                     }
                     Integer present = null;
                     try (ResultSet result = stmt.executeQuery()) {
diff --git 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java
 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java
index cf485bceb2..66010d1a6b 100644
--- 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java
+++ 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Database.java
@@ -158,12 +158,6 @@ public class Database<G> extends Syntax  {
      */
     final EnumSet<CRSEncoding> crsEncodings;
 
-    /**
-     * Whether to allow the addition of new <abbr>CRS</abbr> definitions in 
the {@code SPATIAL_REF_SYS} table.
-     * The default value is {@code false}, which is preferable for read-only 
databases.
-     */
-    public volatile boolean allowAddCRS;
-
     /**
      * {@code true} if this database contains at least one geometry column.
      * This field is initialized by {@link #analyze analyze(…)} and shall not 
be modified after that point.
diff --git 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java
 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java
index 917ed56fc9..19adc0d1f7 100644
--- 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java
+++ 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java
@@ -41,8 +41,10 @@ import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.storage.DataStoreReferencingException;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.referencing.factory.IdentifiedObjectFinder;
 import org.apache.sis.referencing.privy.DefinitionVerifier;
 import org.apache.sis.referencing.privy.ReferencingUtilities;
+import org.apache.sis.metadata.sql.privy.SQLUtilities;
 import org.apache.sis.metadata.sql.privy.SQLBuilder;
 import org.apache.sis.geometry.wrapper.GeometryType;
 import org.apache.sis.system.Modules;
@@ -52,7 +54,6 @@ import org.apache.sis.util.privy.Constants;
 import org.apache.sis.io.wkt.Convention;
 import org.apache.sis.io.wkt.WKTFormat;
 import org.apache.sis.io.wkt.Warnings;
-import org.apache.sis.referencing.factory.IdentifiedObjectFinder;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import org.opengis.metadata.Identifier;
@@ -312,22 +313,24 @@ public class InfoStatements implements Localized, 
AutoCloseable {
      */
     private PreparedStatement prepareSearchCRS(final boolean byAuthorityCode) 
throws SQLException {
         final SpatialSchema schema = database.getSpatialSchema().orElseThrow();
-        final SQLBuilder sql = new 
SQLBuilder(database).append(SQLBuilder.SELECT);
+        final String search, get;
         if (byAuthorityCode) {
-            sql.append(schema.crsIdentifierColumn);
+            search = schema.crsAuthorityCodeColumn;
+            get    = schema.crsIdentifierColumn;
         } else {
-            sql.append(schema.crsAuthorityNameColumn).append(", ")
-               .append(schema.crsAuthorityCodeColumn);
+            search = schema.crsIdentifierColumn;
+            get    = schema.crsAuthorityCodeColumn;
         }
+        final var sql = new SQLBuilder(database).append(SQLBuilder.SELECT)
+                .append(schema.crsAuthorityNameColumn).append(", 
").append(get);
+
         for (CRSEncoding encoding : database.crsEncodings) {
             sql.append(", ").append(schema.crsDefinitionColumn.get(encoding));
         }
         appendFrom(sql, schema.crsTable);
+        sql.append(search).append("=?");
         if (byAuthorityCode) {
-            sql.append(schema.crsAuthorityNameColumn).append("=? AND ")
-               .append(schema.crsAuthorityCodeColumn).append("=?");
-        } else {
-            sql.append(schema.crsIdentifierColumn).append("=?");
+            sql.append(" AND 
LOWER(").append(schema.crsAuthorityNameColumn).append(") LIKE ?");
         }
         return connection.prepareStatement(sql.toString());
     }
@@ -468,7 +471,7 @@ public class InfoStatements implements Localized, 
AutoCloseable {
      * Finds a SRID code from the spatial reference systems table for the 
given CRS.
      * If the database does not support concurrent transactions, then the 
caller is
      * responsible for holding a lock. It may be a read lock or write lock 
depending
-     * on the {@link Database#allowAddCRS} value.
+     * on the {@link Connection#isReadOnly()} value.
      *
      * @param  crs     the CRS for which to find a SRID, or {@code null}.
      * @return SRID for the given CRS, or 0 if the given CRS was null.
@@ -525,24 +528,26 @@ public class InfoStatements implements Localized, 
AutoCloseable {
                 if (sridFromCRS == null) {
                     sridFromCRS = prepareSearchCRS(true);
                 }
-                sridFromCRS.setString(1, authority);
-                sridFromCRS.setInt(2, codeValue);
+                sridFromCRS.setInt(1, codeValue);
+                sridFromCRS.setString(2, SQLUtilities.toLikePattern(authority, 
true));
                 try (ResultSet result = sridFromCRS.executeQuery()) {
                     while (result.next()) {
-                        final int srid = result.getInt(1);
-                        if (sridFounInUse.add(srid)) try {
-                            final Object parsed = parseDefinition(result, 2);
-                            if (Utilities.equalsApproximately(parsed, crs)) {
-                                synchronized (database.cacheOfSRID) {
-                                    database.cacheOfSRID.put(crs, srid);
+                        if (SQLUtilities.filterFalsePositive(authority, 
result.getString(1))) {
+                            final int srid = result.getInt(2);
+                            if (sridFounInUse.add(srid)) try {
+                                final Object parsed = parseDefinition(result, 
3);
+                                if (Utilities.equalsApproximately(parsed, 
crs)) {
+                                    synchronized (database.cacheOfSRID) {
+                                        database.cacheOfSRID.put(crs, srid);
+                                    }
+                                    return srid;
                                 }
-                                return srid;
+                            } catch (ParseException e) {
+                                if (error == null) error = e;
+                                else error.addSuppressed(e);
                             }
-                        } catch (ParseException e) {
-                            if (error == null) error = e;
-                            else error.addSuppressed(e);
+                            done.put(key, Boolean.FALSE);       // Declare 
this "authority:code" pair as not available.
                         }
-                        done.put(key, Boolean.FALSE);       // Declare this 
"authority:code" pair as not available.
                     }
                 }
             }
@@ -563,7 +568,7 @@ public class InfoStatements implements Localized, 
AutoCloseable {
          * If the caller allowed the creation of new rows in that table, 
creates it now.
          * It is caller's responsibility to hold a write lock if needed.
          */
-        if (database.allowAddCRS) {
+        if (!connection.isReadOnly()) {
             String fallback = null;
             int fallbackCode = 0;
             for (final Map.Entry<SimpleImmutableEntry<String,Object>, Boolean> 
entry : done.entrySet()) {
diff --git 
a/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/feature/InfoStatementsTest.java
 
b/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/feature/InfoStatementsTest.java
index da2e2822fe..b93ac7b67a 100644
--- 
a/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/feature/InfoStatementsTest.java
+++ 
b/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/feature/InfoStatementsTest.java
@@ -114,15 +114,16 @@ public final class InfoStatementsTest extends TestCase {
      */
     @Test
     public void testFindSRID() throws Exception {
-        try (InfoStatements info = new InfoStatements(database, connection)) {
-            database.allowAddCRS = false;
+        final Connection c = connection;
+        try (InfoStatements info = new InfoStatements(database, c)) {
+            c.setReadOnly(true);
             final CoordinateReferenceSystem crs = HardCodedCRS.WGS84;
             var e = assertThrows(DataStoreReferencingException.class, () -> 
info.findSRID(crs));
             assertMessageContains(e, crs.getName().getCode());
 
             // Now do the actual insertion.
-            database.allowAddCRS = true;  assertEquals(4326, 
info.findSRID(crs));
-            database.allowAddCRS = false; assertEquals(4326, 
info.findSRID(crs));
+            c.setReadOnly(false); assertEquals(4326, info.findSRID(crs));
+            c.setReadOnly(true);  assertEquals(4326, info.findSRID(crs));
 
             // CRS with the same code (intentional clash with EPSG:4326).
             final CoordinateReferenceSystem clash = new DefaultGeographicCRS(
@@ -130,8 +131,8 @@ public final class InfoStatementsTest extends TestCase {
                            CoordinateReferenceSystem.IDENTIFIERS_KEY, new 
ImmutableIdentifier(null, "FOO", "4326")),
                     HardCodedDatum.SPHERE, null, HardCodedCS.GEODETIC_2D);
 
-            database.allowAddCRS = true;  assertEquals(1, 
info.findSRID(clash));
-            database.allowAddCRS = false; assertEquals(1, 
info.findSRID(clash));
+            c.setReadOnly(false); assertEquals(1, info.findSRID(clash));
+            c.setReadOnly(true);  assertEquals(1, info.findSRID(clash));
         }
     }
 
diff --git 
a/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/postgis/PostgresTest.java
 
b/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/postgis/PostgresTest.java
index 75437a403f..c9f3d458a2 100644
--- 
a/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/postgis/PostgresTest.java
+++ 
b/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/postgis/PostgresTest.java
@@ -122,6 +122,7 @@ public final class PostgresTest extends TestCase {
                 try (Connection connection = database.source.getConnection();
                      ExtendedInfo info = new ExtendedInfo(pg, connection))
                 {
+                    connection.setReadOnly(true);   // For avoiding accidental 
changes to "SPATIAL_REF_SYS" table.
                     testInfoStatements(info);
                     testGeometryGetter(info, connection);
                     testRasterReader(TestRaster.USHORT, info, connection);

Reply via email to