]> source.dussan.org Git - jackcess.git/commitdiff
Better validation of identifier names (disallow invalid characters according to acces...
authorJames Ahlborn <jtahlborn@yahoo.com>
Thu, 4 Dec 2014 01:17:26 +0000 (01:17 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Thu, 4 Dec 2014 01:17:26 +0000 (01:17 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@902 f203690c-595d-4dc9-a70b-905162fa7fd2

src/changes/changes.xml
src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java
src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java
src/main/java/com/healthmarketscience/jackcess/impl/JetFormat.java
src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java
src/main/java/com/healthmarketscience/jackcess/util/ImportUtil.java

index 5137378f74e8d42af3d560cdcf9588529a671405..d128dc3bc150ec6fc69a54a3ee539eb37861b8fd 100644 (file)
       <action dev="jahlborn" type="fix" system="SourceForge2Patches" issue="18">
         Don't double quote already quoted identifiers.
       </action>
+      <action dev="jahlborn" type="update">
+        Better validation of identifier names (disallow invalid characters
+        according to Access naming rules).
+      </action>
     </release>
     <release version="2.0.7" date="2014-11-22">
       <action dev="jahlborn" type="fix" system="SourceForge2" issue="111">
index 26c2c85c9d0ce34ae1b38f56f0f841cb30a899ec..4a55205211d5e1509ffe7129a1b7ffdd749516b6 100644 (file)
@@ -24,8 +24,10 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import com.healthmarketscience.jackcess.impl.DatabaseImpl;
 import com.healthmarketscience.jackcess.impl.IndexData;
 import com.healthmarketscience.jackcess.impl.IndexImpl;
+import com.healthmarketscience.jackcess.impl.JetFormat;
 
 /**
  * Builder style class for constructing an {@link Index}.  See {@link
@@ -133,7 +135,11 @@ public class IndexBuilder
     return this;
   }    
 
-  public void validate(Set<String> tableColNames) {
+  public void validate(Set<String> tableColNames, JetFormat format) {
+
+    DatabaseImpl.validateIdentifierName(
+        getName(), format.MAX_INDEX_NAME_LENGTH, "index");
+
     if(getColumns().isEmpty()) {
       throw new IllegalArgumentException("index " + getName() +
                                          " has no columns");
index 548833b98c59dfa41f39c28e84d64f1d43b39133..2afd394f558e20baf7710c685d779d2ef10f858d 100644 (file)
@@ -56,6 +56,7 @@ import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.TreeSet;
+import java.util.regex.Pattern;
 
 import com.healthmarketscience.jackcess.ColumnBuilder;
 import com.healthmarketscience.jackcess.Cursor;
@@ -248,7 +249,10 @@ public class DatabaseImpl implements Database
   /** the columns to read when getting object propertyes */
   private static Collection<String> SYSTEM_CATALOG_PROPS_COLUMNS =
     new HashSet<String>(Arrays.asList(CAT_COL_ID, CAT_COL_PROPS));
-  
+
+  /** regex matching characters which are invalid in identifier names */
+  private static final Pattern INVALID_IDENTIFIER_CHARS = 
+    Pattern.compile("[\\p{Cntrl}.!`\\]\\[]");
   
   /** the File of the database */
   private final File _file;
@@ -960,8 +964,8 @@ public class DatabaseImpl implements Database
     }
 
     validateIdentifierName(name, getFormat().MAX_TABLE_NAME_LENGTH, "table");
-    validateIdentifierName(linkedDbName, DataType.MEMO.getMaxSize(), 
-                           "linked database");
+    validateName(linkedDbName, DataType.MEMO.getMaxSize(), 
+                 "linked database");
     validateIdentifierName(linkedTableName, getFormat().MAX_TABLE_NAME_LENGTH, 
                            "linked table");
 
@@ -1481,22 +1485,61 @@ public class DatabaseImpl implements Database
   
   /**
    * Validates an identifier name.
+   *
+   * Names of fields, controls, and objects in Microsoft Access:
+   * <ul>
+   * <li>Can include any combination of letters, numbers, spaces, and special
+   *     characters except a period (.), an exclamation point (!), an accent
+   *     grave (`), and brackets ([ ]).</li>
+   * <li>Can't begin with leading spaces.</li>
+   * <li>Can't include control characters (ASCII values 0 through 31).</li>
+   * </ul>
+   * 
    * @usage _advanced_method_
    */
   public static void validateIdentifierName(String name,
                                             int maxLength,
                                             String identifierType)
   {
-    if((name == null) || (name.trim().length() == 0)) {
+    // basic name validation
+    validateName(name, maxLength, identifierType);
+
+    // additional identifier validation
+    if(INVALID_IDENTIFIER_CHARS.matcher(name).find()) {
       throw new IllegalArgumentException(
-          identifierType + " must have non-empty name");
+          identifierType + " name contains invalid characters");
+    }
+
+    // cannot start with spaces
+    if(name.charAt(0) == ' ') {
+      throw new IllegalArgumentException(
+          identifierType + " name cannot start with a space character");
+    }
+  }
+
+  /**
+   * Validates a name.
+   */
+  private static void validateName(String name, int maxLength, String nameType)
+  {
+    if(isBlank(name)) {
+      throw new IllegalArgumentException(
+          nameType + " must have non-blank name");
     }
     if(name.length() > maxLength) {
       throw new IllegalArgumentException(
-          identifierType + " name is longer than max length of " + maxLength +
+          nameType + " name is longer than max length of " + maxLength +
           ": " + name);
     }
   }
+
+  /**
+   * Returns {@code true} if the given string is {@code null} or all blank
+   * space, {@code false} otherwise.
+   */
+  public static boolean isBlank(String name) {
+    return((name == null) || (name.trim().length() == 0));
+  }
   
   @Override
   public String toString() {
index fcb39bd494649094b87c3232e04902dddead831a..4c915e344c3c44d542c5fec70bf8b98d215bdffa 100644 (file)
@@ -264,6 +264,7 @@ public abstract class JetFormat {
   public final int USAGE_MAP_TABLE_BYTE_LENGTH;
 
   public final int MAX_COLUMNS_PER_TABLE;
+  public final int MAX_INDEXES_PER_TABLE;
   public final int MAX_TABLE_NAME_LENGTH;
   public final int MAX_COLUMN_NAME_LENGTH;
   public final int MAX_INDEX_NAME_LENGTH;
@@ -399,6 +400,7 @@ public abstract class JetFormat {
     USAGE_MAP_TABLE_BYTE_LENGTH = defineUsageMapTableByteLength();
 
     MAX_COLUMNS_PER_TABLE = defineMaxColumnsPerTable();
+    MAX_INDEXES_PER_TABLE = defineMaxIndexesPerTable();
     MAX_TABLE_NAME_LENGTH = defineMaxTableNameLength();
     MAX_COLUMN_NAME_LENGTH = defineMaxColumnNameLength();
     MAX_INDEX_NAME_LENGTH = defineMaxIndexNameLength();
@@ -502,6 +504,7 @@ public abstract class JetFormat {
   protected abstract int defineUsageMapTableByteLength();
 
   protected abstract int defineMaxColumnsPerTable();
+  protected abstract int defineMaxIndexesPerTable();
   protected abstract int defineMaxTableNameLength();
   protected abstract int defineMaxColumnNameLength();
   protected abstract int defineMaxIndexNameLength();
@@ -709,6 +712,9 @@ public abstract class JetFormat {
     @Override
     protected int defineMaxColumnsPerTable() { return 255; }
              
+    @Override
+    protected int defineMaxIndexesPerTable() { return 32; }
+             
     @Override
     protected int defineMaxTableNameLength() { return 64; }
              
@@ -938,6 +944,9 @@ public abstract class JetFormat {
       
     @Override
     protected int defineMaxColumnsPerTable() { return 255; }
+             
+    @Override
+    protected int defineMaxIndexesPerTable() { return 32; }
       
     @Override
     protected int defineMaxTableNameLength() { return 64; }
index 8828ac2b1857f77927c5a929238e4d1337ce87da..c7206548b887df5656207e83972f51342832ab08 100644 (file)
@@ -231,11 +231,18 @@ class TableCreator
     }
 
     if(hasIndexes()) {
+
+      if(_indexes.size() > getFormat().MAX_INDEXES_PER_TABLE) {
+        throw new IllegalArgumentException(
+            "Cannot create table with more than " +
+            getFormat().MAX_INDEXES_PER_TABLE + " indexes");
+      }
+
       // now, validate the indexes
       Set<String> idxNames = new HashSet<String>();
       boolean foundPk = false;
       for(IndexBuilder index : _indexes) {
-        index.validate(colNames);
+        index.validate(colNames, getFormat());
         if(!idxNames.add(index.getName().toUpperCase())) {
           throw new IllegalArgumentException("duplicate index name: " +
                                              index.getName());
index ebeb4807344166fe15b60cb93a39b5941180d461..4899d38f1259b6f68ea1455973cbe62f8b300ea2 100644 (file)
@@ -47,6 +47,7 @@ import com.healthmarketscience.jackcess.Database;
 import com.healthmarketscience.jackcess.Table;
 import com.healthmarketscience.jackcess.TableBuilder;
 import com.healthmarketscience.jackcess.impl.ByteUtil;
+import com.healthmarketscience.jackcess.impl.DatabaseImpl;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -451,7 +452,7 @@ public class ImportUtil
     throws IOException
   {
     String line = in.readLine();
-    if (line == null || line.trim().length() == 0) {
+    if(DatabaseImpl.isBlank(line)) {
       return null;
     }