db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mi...@apache.org
Subject svn commit: r561189 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/types/XML.java engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java
Date Mon, 30 Jul 2007 23:50:55 GMT
Author: mikem
Date: Mon Jul 30 16:50:54 2007
New Revision: 561189

URL: http://svn.apache.org/viewvc?view=rev&rev=561189
Log:
DERBY-2350

Fix XML in triggers by changing code generation to generate different code
in the case of XML of the form when referencing old and new column values.:
XMLPARSE(DOCUMENT
CAST (org.apache.derby.iapi.db.Factory::
getTriggerExecutionContext().getNewRow().
getString(<colPosition>) AS CLOB)
PRESERVE WHITESPACE) 

Also change XML.setValueFromResult() to validate the character stream and
set the xType as appropriate after parsing the stream.  


Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/XML.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/XML.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/XML.java?view=diff&rev=561189&r1=561188&r2=561189
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/XML.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/XML.java Mon Jul 30 16:50:54
2007
@@ -139,6 +139,8 @@
      */
     private boolean containsTopLevelAttr;
 
+    private SqlXmlUtil tmpUtil;
+
     /**
      * Default constructor.
      */
@@ -303,7 +305,72 @@
     {
         if (xmlStringValue == null)
             xmlStringValue = new SQLChar();
-        xmlStringValue.setValue(resultSet.getString(colNumber));
+
+        String valAsStr = resultSet.getString(colNumber);
+
+        /* As there is no guarantee that the specified column within
+         * resultSet is well-formed XML (is there??), we have to try
+         * to parse it in order to set the "xType" field correctly.
+         * This is required to ensure that we only store well-formed
+         * XML on disk (see "normalize()" method of this class).  So
+         * create an instance of SqlXmlUtil and use that to see if the
+         * text satisifies the requirements of a well-formed DOCUMENT.
+         *
+         * RESOLVE: If there is anyway to guarantee that the column
+         * is in fact well-formed XML then we can skip all of this
+         * logic and simply set xType to XML_DOC_ANY.  But do we
+         * have such a guarantee...?
+         */
+        if (tmpUtil == null)
+        {
+            try {
+
+                tmpUtil = new SqlXmlUtil();
+
+            } catch (StandardException se) {
+
+                if (SanityManager.DEBUG)
+                {
+                    SanityManager.THROWASSERT(
+                        "Failed to instantiate SqlXmlUtil for XML parsing.");
+                }
+
+                /* If we failed to get a SqlXmlUtil then we can't parse
+                 * the string, which means we don't know if it constitutes
+                 * a well-formed XML document or not.  In this case we
+                 * set the value, but intentionally leave xType as -1
+                 * so that the resultant value canNOT be stored on disk.
+                 */
+                xmlStringValue.setValue(valAsStr);
+                setXType(-1);
+                return;
+
+            }
+        }
+
+        try {
+
+            /* The following call parses the string into a DOM and
+             * then serializes it, which is exactly what we do for
+             * normal insertion of XML values.  If the parse finishes
+             * with no error then we know the type is XML_DOC_ANY,
+             * so set it.
+             */
+            valAsStr = tmpUtil.serializeToString(valAsStr);
+            xmlStringValue.setValue(valAsStr);
+            setXType(XML_DOC_ANY);
+
+        } catch (Throwable t) {
+
+            /* It's possible that the string value was either 1) an
+             * XML SEQUENCE or 2) not XML at all.  We don't know
+             * which one it was, so make xType invalid to ensure this
+             * field doesn't end up on disk.
+             */
+            xmlStringValue.setValue(valAsStr);
+            setXType(-1);
+
+        }
     }
 
     /**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java?view=diff&rev=561189&r1=561188&r2=561189
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTriggerNode.java
Mon Jul 30 16:50:54 2007
@@ -570,15 +570,17 @@
 	) throws StandardException
 	{
 		ColumnDescriptor colDesc = null;
-		if ((colDesc = triggerTableDescriptor.getColumnDescriptor(colName)) == null)
+		if ((colDesc = triggerTableDescriptor.getColumnDescriptor(colName)) == 
+                null)
 		{
-			throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND, tabName+"."+colName);
+			throw StandardException.newException(
+                SQLState.LANG_COLUMN_NOT_FOUND, tabName+"."+colName);
 		}
 
 		/*
 		** Generate something like this:
 		**
-		** 		cast (org.apache.derby.iapi.db.Factory::
+		** 		CAST (org.apache.derby.iapi.db.Factory::
 		**			getTriggerExecutionContext().getNewRow().
 		**				getObject(<colPosition>) AS DECIMAL(6,2))
         **
@@ -594,27 +596,73 @@
 		** something like
 		**
 		**		CREATE TRIGGER ... INSERT INTO T length(Column), ...
-		*/
-		StringBuffer methodCall = new StringBuffer();
-		methodCall.append("CAST (org.apache.derby.iapi.db.Factory::getTriggerExecutionContext().");
-		methodCall.append(isOldTable ? "getOldRow()" : "getNewRow()");
-		methodCall.append(".getObject(");
-        methodCall.append(colDesc.getPosition());
-        methodCall.append(") AS ");
-		DataTypeDescriptor dts = colDesc.getType();
-		TypeId typeId = dts.getTypeId();
+        **
+        */
 
-		/*
-		** getSQLString() returns <typeName> 
-		** for user types, so call getSQLTypeName in that
-		** case.
-		*/
-		methodCall.append(
-		  (typeId.userType() ? typeId.getSQLTypeName() : dts.getSQLstring()));
-        
-        methodCall.append(") ");
+		DataTypeDescriptor  dts     = colDesc.getType();
+		TypeId              typeId  = dts.getTypeId();
+
+        if (!typeId.isXMLTypeId())
+        {
+
+            StringBuffer methodCall = new StringBuffer();
+            methodCall.append(
+                "CAST (org.apache.derby.iapi.db.Factory::getTriggerExecutionContext().");
+            methodCall.append(isOldTable ? "getOldRow()" : "getNewRow()");
+            methodCall.append(".getObject(");
+            methodCall.append(colDesc.getPosition());
+            methodCall.append(") AS ");
+
+            /*
+            ** getSQLString() returns <typeName> 
+            ** for user types, so call getSQLTypeName in that
+            ** case.
+            */
+            methodCall.append(
+                (typeId.userType() ? 
+                     typeId.getSQLTypeName() : dts.getSQLstring()));
+            
+            methodCall.append(") ");
+
+            return methodCall.toString();
+        }
+        else
+        {
+            /*  DERBY-2350
+            **
+            **  Triggers currently use jdbc 1.2 to access columns.  The default
+            **  uses getObject() which is not supported for an XML type until
+            **  jdbc 4.  In the meantime use getString() and then call 
+            **  XMLPARSE() on the string to get the type.  See Derby issue and
+            **  http://wiki.apache.org/db-derby/TriggerImplementation , for
+            **  better long term solutions.  Long term I think changing the
+            **  trigger architecture to not rely on jdbc, but instead on an
+            **  internal direct access interface to execution nodes would be
+            **  best future direction, but believe such a change appropriate
+            **  for a major release, not a bug fix.
+            **
+            **  Rather than the above described code generation, use the 
+            **  following for XML types to generate an XML column from the
+            **  old or new row.
+            ** 
+            **          XMLPARSE(DOCUMENT
+            **              CAST (org.apache.derby.iapi.db.Factory::
+            **                  getTriggerExecutionContext().getNewRow().
+            **                      getString(<colPosition>) AS CLOB)  
+            **                        PRESERVE WHITESPACE)
+            */
+
+            StringBuffer methodCall = new StringBuffer();
+            methodCall.append("XMLPARSE(DOCUMENT CAST( ");
+            methodCall.append(
+                "org.apache.derby.iapi.db.Factory::getTriggerExecutionContext().");
+            methodCall.append(isOldTable ? "getOldRow()" : "getNewRow()");
+            methodCall.append(".getString(");
+            methodCall.append(colDesc.getPosition());
+            methodCall.append(") AS CLOB) PRESERVE WHITESPACE ) ");
 
-		return methodCall.toString();
+            return methodCall.toString();
+        }
 	}
 
 	/*

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java?view=diff&rev=561189&r1=561188&r2=561189
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/TriggerTest.java
Mon Jul 30 16:50:54 2007
@@ -548,10 +548,6 @@
         int jdbcType = DatabaseMetaDataTest.getJDBCType(type);
         int precision = DatabaseMetaDataTest.getPrecision(jdbcType, type);
 
-        // BUG DERBY-2350  - remove this check & return to see the issue.      
-        if (jdbcType == JDBC.SQLXML)
-            return;
-        
         // BUG DERBY-2349 - remove this check & return to see the issue.
         if (jdbcType == Types.BLOB)
             return; 
@@ -602,10 +598,6 @@
         int jdbcType = DatabaseMetaDataTest.getJDBCType(type);
         int precision = DatabaseMetaDataTest.getPrecision(jdbcType, type);
 
-        // BUG DERBY-2350 - need insert case to work first
-        if (jdbcType == JDBC.SQLXML)
-            return;
-
         // BUG DERBY-2349 - need insert case to work first
         if (jdbcType == Types.BLOB)
             return;
@@ -619,7 +611,11 @@
         Random r = new Random();
         
         PreparedStatement ps = prepareStatement(
-            "UPDATE T_MAIN SET V = ? WHERE ID >= ? AND ID <= ?");
+            (jdbcType == JDBC.SQLXML
+                ? "UPDATE T_MAIN SET V = " +
+                  "XMLPARSE(DOCUMENT CAST (? AS CLOB) PRESERVE WHITESPACE)"
+                : "UPDATE T_MAIN SET V = ?")
+            + " WHERE ID >= ? AND ID <= ?");
         
         // Single row update of row 3
         setRandomValue(r, ps, 1, jdbcType, precision);
@@ -716,10 +712,6 @@
     {
         int jdbcType = DatabaseMetaDataTest.getJDBCType(type);
         int precision = DatabaseMetaDataTest.getPrecision(jdbcType, type);
-
-        // BUG DERBY-2350 - need insert case to work first
-        if (jdbcType == JDBC.SQLXML)
-            return;
 
         // BUG DERBY-2349 - need insert case to work first
         if (jdbcType == Types.BLOB)



Mime
View raw message