]> source.dussan.org Git - jackcess.git/commitdiff
fix creation of calculated fields
authorJames Ahlborn <jtahlborn@yahoo.com>
Tue, 16 Sep 2014 02:34:51 +0000 (02:34 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Tue, 16 Sep 2014 02:34:51 +0000 (02:34 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@876 f203690c-595d-4dc9-a70b-905162fa7fd2

src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java
src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java
src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java
src/test/java/com/healthmarketscience/jackcess/impl/CalcFieldTest.java

index 628df86c37a35a8938599837244813d780d8bd33..bbfca27edec8a6c08f7cc14d6ac18a621ed2fa4d 100644 (file)
@@ -215,6 +215,11 @@ public class ColumnBuilder {
     return putProperty(PropertyMap.RESULT_TYPE_PROP, getType().getValue());
   }
 
+  public boolean isVariableLength() {
+    // calculated columns are written as var len
+    return(getType().isVariableLength() || isCalculated());
+  }
+
   /**
    * Sets whether of not the new column allows unicode compression.
    */
index e548da20d47ac228dfeca6dab4fd3ab96948ad17..727818a2b0a30d79d2c19218b09252ae0c0b0f47 100644 (file)
@@ -40,7 +40,9 @@ class CalculatedColumnUtil
   // offset to the actual data
   private static final int CALC_DATA_OFFSET = CALC_DATA_LEN_OFFSET + 4;
   // total amount of extra bytes added for calculated values
-  private static final int CALC_EXTRA_DATA_LEN = 23;
+  static final int CALC_EXTRA_DATA_LEN = 23;
+  // ms access seems to define all fixed-len calc fields as this length
+  static final short CALC_FIXED_FIELD_LEN = 39;
 
   // fully encode calculated BOOLEAN "true" value
   private static final byte[] CALC_BOOL_TRUE = wrapCalculatedValue(
@@ -278,7 +280,8 @@ class CalculatedColumnUtil
       throws IOException
     {
       int totalDataLen = Math.min(CALC_EXTRA_DATA_LEN + 16 + 4, getLength());
-      int dataLen = totalDataLen - CALC_EXTRA_DATA_LEN;
+      // data length must be multiple of 4
+      int dataLen = toMul4(totalDataLen - CALC_EXTRA_DATA_LEN);
       ByteBuffer buffer = prepareWrappedCalcValue(dataLen, order);
 
       writeCalcNumericValue(buffer, obj, dataLen);
@@ -293,7 +296,7 @@ class CalculatedColumnUtil
       // numeric bytes need to be a multiple of 4 and we currently handle at
       // most 16 bytes
       int numByteLen = ((totalLen > 0) ? totalLen : buffer.remaining()) - 2;
-      numByteLen = Math.min((numByteLen / 4) * 4, 16);
+      numByteLen = Math.min(toMul4(numByteLen), 16);
 
       byte scale = buffer.get();
       boolean negate = (buffer.get() != 0);
@@ -371,6 +374,10 @@ class CalculatedColumnUtil
         ByteUtil.swap8Bytes(bytes, pos);
       }
     }
+
+    private static int toMul4(int val) {
+      return ((val / 4) * 4);
+    }
   }
 
 }
index 40c8c6c44c3028e2d3a528351ee848d4900e7f46..0fbe7c2f068031c77ff820736001d223c25b50ff 100644 (file)
@@ -1264,7 +1264,7 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
    */
   private static byte getColumnBitFlags(ColumnBuilder col) {
     byte flags = UNKNOWN_FLAG_MASK;
-    if(!col.getType().isVariableLength()) {
+    if(!col.isVariableLength()) {
       flags |= FIXED_LEN_FLAG_MASK;
     }
     if(col.isAutoNumber()) {
@@ -1377,7 +1377,7 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
   public static short countVariableLength(List<ColumnBuilder> columns) {
     short rtn = 0;
     for (ColumnBuilder col : columns) {
-      if (col.getType().isVariableLength()) {
+      if (col.isVariableLength()) {
         rtn++;
       }
     }
@@ -1390,10 +1390,10 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
    *         found in the list
    * @usage _advanced_method_
    */
-  public static short countNonLongVariableLength(List<ColumnBuilder> columns) {
+  private static short countNonLongVariableLength(List<ColumnBuilder> columns) {
     short rtn = 0;
     for (ColumnBuilder col : columns) {
-      if (col.getType().isVariableLength() && !col.getType().isLongValue()) {
+      if (col.isVariableLength() && !col.getType().isLongValue()) {
         rtn++;
       }
     }
@@ -1572,7 +1572,7 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
       buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER);  //constant magic number
       buffer.putShort(col.getColumnNumber());  //Column Number
 
-      if (col.getType().isVariableLength()) {
+      if(col.isVariableLength()) {
         if(!col.getType().isLongValue()) {
           buffer.putShort(variableOffset++);
         } else {
@@ -1614,7 +1614,7 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
       buffer.putInt(0); //Unknown, but always 0.
 
       //Offset for fixed length columns
-      if (col.getType().isVariableLength()) {
+      if(col.isVariableLength()) {
         buffer.putShort((short) 0);
       } else {
         buffer.putShort(fixedOffset);
@@ -1622,7 +1622,17 @@ public class ColumnImpl implements Column, Comparable<ColumnImpl> {
       }
 
       if(!col.getType().isLongValue()) {
-        buffer.putShort(col.getLength()); //Column length
+        short length = col.getLength();
+        if(col.isCalculated()) {
+          // calced columns have additional value overhead
+          if(!col.getType().isVariableLength() || 
+             col.getType().getHasScalePrecision()) {
+            length = CalculatedColumnUtil.CALC_FIXED_FIELD_LEN; 
+          } else {
+            length += CalculatedColumnUtil.CALC_EXTRA_DATA_LEN;
+          }
+        }
+        buffer.putShort(length); //Column length
       } else {
         buffer.putShort((short)0x0000); // unused
       }
index d230cba77f6ddec97a9dc466ed29517ccc45e3d5..26b2390fe594d313c90d737849ca713ec2f56f75 100644 (file)
@@ -20,6 +20,7 @@ USA
 package com.healthmarketscience.jackcess.impl;
 
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -75,11 +76,19 @@ public class CalcFieldTest extends TestCase
         .addColumn(new ColumnBuilder("id", DataType.LONG)
                    .setAutoNumber(true))
         .addColumn(new ColumnBuilder("data", DataType.TEXT))
-        .addColumn(new ColumnBuilder("calc_data", DataType.TEXT)
+        .addColumn(new ColumnBuilder("calc_text", DataType.TEXT)
                    .setCalculatedInfo("[id] & \"_\" & [data]"))
+        .addColumn(new ColumnBuilder("calc_memo", DataType.MEMO)
+                   .setCalculatedInfo("[id] & \"_\" & [data]"))
+        .addColumn(new ColumnBuilder("calc_bool", DataType.BOOLEAN)
+                   .setCalculatedInfo("[id] > 0"))
+        .addColumn(new ColumnBuilder("calc_long", DataType.LONG)
+                   .setCalculatedInfo("[id] + 1"))
+        .addColumn(new ColumnBuilder("calc_numeric", DataType.NUMERIC)
+                   .setCalculatedInfo("[id] / 0.03"))
         .toTable(db);
 
-      Column col = t.getColumn("calc_data");
+      Column col = t.getColumn("calc_text");
       assertTrue(col.isCalculated());
       assertEquals("[id] & \"_\" & [data]", col.getProperties().getValue(
                        PropertyMap.EXPRESSION_PROP));
@@ -87,14 +96,31 @@ public class CalcFieldTest extends TestCase
                    col.getProperties().getValue(
                        PropertyMap.RESULT_TYPE_PROP));
 
-      t.addRow(Column.AUTO_NUMBER, "foo", "1_foo");
+      String longStr = createString(1000);
+      BigDecimal bd1 = new BigDecimal("-1234.5678");
+      BigDecimal bd2 = new BigDecimal("0.0234");
+
+      t.addRow(Column.AUTO_NUMBER, "foo", "1_foo", longStr, true, 2, bd1);
+      t.addRow(Column.AUTO_NUMBER, "bar", "2_bar", longStr, false, -37, bd2);
 
       List<? extends Map<String, Object>> expectedRows =
         createExpectedTable(
             createExpectedRow(
                 "id", 1,
                 "data", "foo",
-                "calc_data", "1_foo"));
+                "calc_text", "1_foo",
+                "calc_memo", longStr,
+                "calc_bool", true,
+                "calc_long", 2,
+                "calc_numeric", bd1),
+            createExpectedRow(
+                "id", 2,
+                "data", "bar",
+                "calc_text", "2_bar",
+                "calc_memo", longStr,
+                "calc_bool", false,
+                "calc_long", -37,
+                "calc_numeric", bd2));
 
       assertTable(expectedRows, t);