You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by da...@apache.org on 2007/10/15 13:24:16 UTC

svn commit: r584738 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/sql/ engine/org/apache/derby/iapi/sql/compile/ engine/org/apache/derby/impl/sql/compile/ testing/org/apache/derbyTesting/functionTests/tests/lang/

Author: dag
Date: Mon Oct 15 04:24:15 2007
New Revision: 584738

URL: http://svn.apache.org/viewvc?rev=584738&view=rev
Log:
DERBY-3073 SQL roles: add parser support

Patch DERBY-3073c. This patch adds support for roles to the parser, plus tests for the
new syntax.

Added:
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/StatementType.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/C_NodeTypes.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NodeFactoryImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/StatementType.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/StatementType.java?rev=584738&r1=584737&r2=584738&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/StatementType.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/StatementType.java Mon Oct 15 04:24:15 2007
@@ -51,7 +51,8 @@
 	
 	public static final int SET_SCHEMA_USER = 1;
 	public static final int SET_SCHEMA_DYNAMIC = 2;
-	
+
+    public static final int SET_ROLE_DYNAMIC = 1;	
 }
 
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/C_NodeTypes.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/C_NodeTypes.java?rev=584738&r1=584737&r2=584738&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/C_NodeTypes.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/C_NodeTypes.java Mon Oct 15 04:24:15 2007
@@ -217,6 +217,18 @@
     static final int XML_EXISTS_OPERATOR_NODE = 202;
     static final int XML_QUERY_OPERATOR_NODE = 203;
 
+    // Roles
+    static final int CURRENT_ROLE_NODE = 210;
+    static final int CREATE_ROLE_NODE = 211;
+    static final int SET_ROLE_NODE = 212;
+    static final int SET_ROLE_DYNAMIC = 213;
+    static final int DROP_ROLE_NODE = 214;
+    static final int GRANT_ROLE_NODE = 215;
+    static final int REVOKE_ROLE_NODE = 216;
+
+    // Final value in set, keep up to date!
+    static final int FINAL_VALUE = REVOKE_ROLE_NODE;
+
     /**
      * Extensions to this interface can use nodetypes > MAX_NODE_TYPE with out fear of collision
      * with C_NodeTypes

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NodeFactoryImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NodeFactoryImpl.java?rev=584738&r1=584737&r2=584738&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NodeFactoryImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/NodeFactoryImpl.java Mon Oct 15 04:24:15 2007
@@ -72,7 +72,8 @@
 	/* Do join order optimization by default */
 	private Boolean joinOrderOptimization = Boolean.TRUE;
 
-	private final ClassInfo[]	nodeCi = new ClassInfo[205];
+	private final ClassInfo[]	nodeCi =
+		new ClassInfo[C_NodeTypes.FINAL_VALUE+1];
 	
 	private static final Vector emptyVector = new Vector(0);
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj?rev=584738&r1=584737&r2=584738&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj Mon Oct 15 04:24:15 2007
@@ -322,7 +322,7 @@
 		Utility method for checking that the underlying database uses SQL standard
 		permission checking (GRANT/REVOKE).
 
-		@param command "GRANT" or "REVOKE"
+		@param command "GRANT", "REVOKE", "CREATE/DROP/SET ROLE" or CURRENT_ROLE
     */
 	private void checkSqlStandardAccess( String command) throws StandardException
 	{
@@ -1675,6 +1675,16 @@
 		    }
 		}
     }
+
+	boolean isPrivilegeKeywordExceptTrigger(int tokenKind) {
+		return (tokenKind == SELECT ||
+				tokenKind == DELETE ||
+				tokenKind == INSERT ||
+				tokenKind == UPDATE ||
+				tokenKind == REFERENCES ||
+				tokenKind == EXECUTE ||
+				tokenKind == ALL);
+	}
 }
 
 PARSER_END(SQLParser)
@@ -2222,6 +2232,7 @@
 	<BOOLEAN: "boolean">
 |	<CALL: "call">
 |	<CURDATE: "curdate">
+|	<CURRENT_ROLE: "current_role">
 |	<CURTIME: "curtime">
 |   <DATABASE: "database">
 |	<GET_CURRENT_CONNECTION: "getCurrentConnection">
@@ -2229,6 +2240,8 @@
 |	<LONGINT: "bigint">
 |	<LONG: "long">
 |	<LTRIM: "ltrim">
+|	<NONE: "none">
+|	<ROLE: "role">
 |	<RTRIM: "rtrim">
 |	<SUBSTR:	"substr">
 |	<XML:	"xml">
@@ -2767,7 +2780,8 @@
             statementNode = schemaDefinition() |
             statementNode = viewDefinition(beginToken) |
             statementNode = triggerDefinition() |
-            statementNode = synonymDefinition()
+            statementNode = synonymDefinition() |
+            statementNode = roleDefinition()
         )
         {
         }
@@ -2803,7 +2817,8 @@
             statementNode = dropIndexStatement() |
             statementNode = dropAliasStatement() |
             statementNode = dropViewStatement() |
-            statementNode = dropTriggerStatement() 
+            statementNode = dropTriggerStatement() |
+            statementNode = dropRoleStatement()
         )
 	{
 		return statementNode;
@@ -2842,7 +2857,8 @@
         (
                 statementNode = setIsolationStatement() |
 	        statementNode = setSchemaStatement() |
-		statementNode = setMessageLocaleStatement()
+            statementNode = setMessageLocaleStatement() |
+            statementNode = setRoleStatement()
         )
 	{
 		return statementNode;
@@ -7344,6 +7360,11 @@
 	{
 		return parm;
 	}
+|
+	parm = currentRoleNode()
+	{
+		return parm;
+	}
 }
 
 ValueNode
@@ -7374,6 +7395,23 @@
 }
 
 
+/*
+ * <A NAME="currentRoleNode">currentRoleNode</A>
+ */
+ValueNode
+currentRoleNode() throws StandardException :
+{
+}
+{
+	<CURRENT_ROLE>
+	{
+		checkSqlStandardAccess("CURRENT_ROLE");
+		checkVersion( DataDictionary.DD_VERSION_DERBY_10_4, "ROLES");
+		return (ValueNode) nodeFactory.getNode(
+			C_NodeTypes.CURRENT_ROLE_NODE,
+			getContextManager());
+	}
+}
 
 
 /*
@@ -9581,6 +9619,34 @@
 	)
 }
 
+
+/*
+ * <A NAME="roleDefinition">roleDefinition</A>
+ */
+StatementNode
+roleDefinition() throws StandardException :
+{
+	String	roleName = null;
+}
+{
+	/*
+	 * CREATE ROLE
+	 *
+	 * Not currently supported: [ WITH ADMIN <grantor>]
+	 */
+	<ROLE> roleName = identifier(Limits.MAX_IDENTIFIER_LENGTH, true)
+	{
+		checkSqlStandardAccess("CREATE ROLE");
+		checkVersion( DataDictionary.DD_VERSION_DERBY_10_4, "ROLES");
+
+		return (StatementNode) nodeFactory.getNode(
+			C_NodeTypes.CREATE_ROLE_NODE,
+			roleName,
+			getContextManager());
+	}
+}
+
+
 /*
  * <A NAME="tableDefinition">tableDefinition</A>
  */
@@ -10598,6 +10664,11 @@
 		return value;
 	}
 |
+	value = currentRoleNode()
+	{
+		return value;
+	}
+|
 	LOOKAHEAD({
 				getToken(1).kind == DATE ||
                 getToken(1).kind == TIME ||
@@ -11322,6 +11393,78 @@
 }
 
 
+/*
+ * <A NAME="setRoleStatement">setRoleStatement</A>
+ */
+StatementNode
+setRoleStatement() throws StandardException :
+{
+	StatementNode role;
+}
+{
+	/*
+	 * SET ROLE { <rolename> | NONE | ? }
+	 *
+	 * Can also be prepared with ? argument, cf. SET SCHEMA.
+	 */
+	<ROLE> role = setRoleSpecification()
+	{
+		if (parameterList != null && parameterList.size() > 0)
+		{
+			setUpAndLinkParameters();
+			// set the type of parameter node, it should be a varchar
+			// max Limits.MAX_IDENTIFIER_LENGTH - non nullable
+			ParameterNode p = (ParameterNode)parameterList.elementAt(0);
+			p.setType(new DataTypeDescriptor
+					  (TypeId.getBuiltInTypeId(Types.VARCHAR),
+					   false,
+					   Limits.MAX_IDENTIFIER_LENGTH));
+		}
+		return role;
+	}
+}
+
+
+/*
+ * <A NAME="setRoleSpecification">setRoleSpecification</A>
+ */
+StatementNode
+setRoleSpecification() throws StandardException :
+{
+	String roleName = null;
+        checkSqlStandardAccess("SET ROLE");
+        checkVersion( DataDictionary.DD_VERSION_DERBY_10_4, "ROLES");
+}
+{
+	<NONE>
+	{
+		return (StatementNode) nodeFactory.getNode
+			(C_NodeTypes.SET_ROLE_NODE,
+			 roleName,
+			 null,
+			 getContextManager());
+	}
+|
+	roleName = identifier(Limits.MAX_IDENTIFIER_LENGTH, true)
+	{
+		return (StatementNode) nodeFactory.getNode
+			(C_NodeTypes.SET_ROLE_NODE,
+			 roleName,
+			 null,
+			 getContextManager());
+	}
+|
+	dynamicParameterSpecification()
+	{
+		return (StatementNode) nodeFactory.getNode
+			(C_NodeTypes.SET_ROLE_NODE,
+			 null,
+			 ReuseFactory.getInteger(StatementType.SET_ROLE_DYNAMIC),
+			 getContextManager());
+	}
+}
+
+
 StatementNode
 setSchemaStatement() throws StandardException :
 {
@@ -11923,6 +12066,32 @@
 	}
 }
 
+
+/*
+ * <A NAME="dropRoleStatement">dropRoleStatement</A>
+ */
+StatementNode
+dropRoleStatement() throws StandardException :
+{
+	String roleName;
+}
+{
+	/*
+	 * DROP ROLE <rolename>
+	 */
+	<ROLE> roleName = identifier(Limits.MAX_IDENTIFIER_LENGTH, true)
+	{
+		checkSqlStandardAccess("DROP ROLE");
+		checkVersion( DataDictionary.DD_VERSION_DERBY_10_4, "ROLES");
+
+		return (StatementNode) nodeFactory.getNode(
+			C_NodeTypes.DROP_ROLE_NODE,
+			roleName,
+			getContextManager());
+	}
+}
+
+
 StatementNode
 dropSchemaStatement() throws StandardException :
 {
@@ -12454,9 +12623,33 @@
 StatementNode
 grantStatement() throws StandardException :
 {
-    StatementNode node;
+	StatementNode node;
 }
 {
+	/* TRIGGER is a non-reserved word, so it is allowed as role
+	 * identifier.	This gives a parser problem in determining if
+	 * a grant statement is a grant role or grant privilege
+	 * statement. In a grant privilege statement, all the other
+	 * grantable privileges are reserved keywords, except
+	 * TRIGGER. So, if we encounter GRANT TRIGGER, we need to look
+	 * one or two more tokens ahead to determine what kind of
+	 * grant statement we are seeing:
+	 *
+	 * privilege grant:
+	 *	  grant trigger , <any of rest of privilege keywords>
+	 *	  grant trigger on ..
+	 *	  grant <any of rest of privilege keywords>
+	 * role grant:
+	 *	  grant trigger to ..
+	 *	  grant trigger, <NOT any of  rest of privilege keywords>
+	 *	  grant <NOT any of rest of privilege keywords>
+	 */
+	LOOKAHEAD( { getToken(1).kind == GRANT &&
+				((getToken(2).kind == TRIGGER &&
+				  ((getToken(3).kind == COMMA &&
+					isPrivilegeKeywordExceptTrigger(getToken(4).kind)) ||
+				   getToken(3).kind == ON)) ||
+				 isPrivilegeKeywordExceptTrigger(getToken(2).kind)) } )
 	<GRANT>
 	{
 		checkVersion( DataDictionary.DD_VERSION_DERBY_10_2, "GRANT");
@@ -12466,6 +12659,24 @@
 	{
 		return node;
 	}
+|
+	LOOKAHEAD( { getToken(1).kind == GRANT &&
+				((getToken(2).kind == TRIGGER &&
+				  ((getToken(3).kind == COMMA &&
+					!isPrivilegeKeywordExceptTrigger(getToken(4).kind)) ||
+				   getToken(3).kind == TO)) ||
+				 !isPrivilegeKeywordExceptTrigger(getToken(2).kind)) } )
+	<GRANT>
+	{
+		checkSqlStandardAccess("GRANT <role>");
+		checkVersion( DataDictionary.DD_VERSION_DERBY_10_4, "ROLES");
+	}
+	( node = roleGrantStatement() )
+	{
+		checkVersion( DataDictionary.DD_VERSION_DERBY_10_2, "GRANT");
+		checkSqlStandardAccess( "GRANT");
+		return node;
+	}
 }
 
 /*
@@ -12684,6 +12895,67 @@
     }
 }
 
+
+/*
+ * <A NAME="roleGrantStatement">roleGrantStatement</A>
+ */
+StatementNode
+roleGrantStatement() throws StandardException :
+{
+	List rolesGranted;
+	List grantees;
+}
+{
+	/*
+	 * GRANT <rolename> {, <rolename>}* TO <authentication identifier>
+	 *									{, <authentication identifier>}*
+	 *
+	 * not implemented: WITH ADMIN OPTION, GRANTED BY clauses
+	 */
+	rolesGranted = roleList()
+	<TO>
+	grantees = granteeList()
+	{
+		return (StatementNode) nodeFactory.getNode
+			(C_NodeTypes.GRANT_ROLE_NODE,
+			 rolesGranted,
+			 grantees,
+			 getContextManager());
+	}
+}
+
+
+/*
+ * <A NAME="roleList">roleList</A>
+ */
+List roleList() throws StandardException :
+{
+    ArrayList list = new ArrayList();
+}
+{
+    roleElement(list) ( <COMMA> roleElement(list) ) *
+    {
+        return list;
+    }
+}
+
+
+/*
+ * <A NAME="roleElement">roleElement</A>
+ */
+void
+roleElement( List list)  throws StandardException :
+{
+    String str;
+}
+{
+    str = identifier(Limits.MAX_IDENTIFIER_LENGTH, true)
+    {
+        list.add(str);
+    }
+}
+
+
 /*
  * <A NAME="revokeStatement">revokeStatement</A>
  */
@@ -12693,15 +12965,48 @@
     StatementNode node;
 }
 {
-    <REVOKE>
-      {
-          checkVersion( DataDictionary.DD_VERSION_DERBY_10_2, "REVOKE");
-          checkSqlStandardAccess( "REVOKE");
-      }
-    ( node = tableRevokeStatement() | node = routineRevokeStatement() )
-    {
-        return node;
-    }
+	/* See look-ahead explanation in grantStatement.
+	 *
+	 * privilege revoke:
+	 *	  revoke trigger , <any of rest of privilege keywords>
+	 *	  revoke trigger on ..
+	 *	  revoke <any of rest of privilege keywords>
+	 * role revoke:
+	 *	  revoke trigger from ..
+	 *	  revoke trigger, <NOT any of  rest of privilege keywords>
+	 *	  revoke <NOT any of rest of privilege keywords>
+	 */
+	LOOKAHEAD( { getToken(1).kind == REVOKE &&
+				((getToken(2).kind == TRIGGER &&
+				  ((getToken(3).kind == COMMA &&
+					isPrivilegeKeywordExceptTrigger(getToken(4).kind)) ||
+				   getToken(3).kind == ON)) ||
+				 isPrivilegeKeywordExceptTrigger(getToken(2).kind)) } )
+	<REVOKE>
+	{
+		checkVersion( DataDictionary.DD_VERSION_DERBY_10_2, "REVOKE");
+		checkSqlStandardAccess( "REVOKE");
+	}
+	( node = tableRevokeStatement() | node = routineRevokeStatement() )
+	{
+		return node;
+	}
+|
+	LOOKAHEAD( { getToken(1).kind == REVOKE &&
+				((getToken(2).kind == TRIGGER &&
+				  ((getToken(3).kind == COMMA &&
+					!isPrivilegeKeywordExceptTrigger(getToken(4).kind)) ||
+				   getToken(3).kind == FROM)) ||
+				 !isPrivilegeKeywordExceptTrigger(getToken(2).kind)) } )
+	<REVOKE>
+	{
+		checkSqlStandardAccess("REVOKE <role>");
+		checkVersion( DataDictionary.DD_VERSION_DERBY_10_4, "ROLES");
+	}
+	( node = roleRevokeStatement() )
+	{
+		return node;
+	}
 }
 
 /*
@@ -12747,6 +13052,29 @@
     }
 }// end of routineRevokeStatement
 
+
+/*
+ * <A NAME="roleRevokeStatement">roleRevokeStatement</A>
+ */
+StatementNode
+roleRevokeStatement() throws StandardException :
+{
+	List rolesRevokeed;
+	List grantees;
+}
+{
+	rolesRevokeed = roleList()
+	<FROM>
+	grantees = granteeList()
+	{
+		return (StatementNode) nodeFactory.getNode
+			(C_NodeTypes.REVOKE_ROLE_NODE,
+			 rolesRevokeed,
+			 grantees,
+			 getContextManager());
+	}
+}
+
 /*
  * <A NAME="identifier">identifier</A>
  */
@@ -12970,6 +13298,7 @@
 |	tok = <NVARCHAR> 
 |	tok = <NEXT>
 |	tok = <NO>
+|   tok = <NONE>
 |	tok = <NOT>
 |	tok = <NULL>
 |	tok = <NULLIF>
@@ -13049,6 +13378,7 @@
 	/* Additional JSQL reserved keywords -- non-SQL92 reserved Keywords */
 |	tok = <BOOLEAN>
 |	tok = <CALL>
+|   tok = <CURRENT_ROLE>
 |	tok = <EXPLAIN>
 |	tok = <LONGINT>
 |	tok = <LTRIM>
@@ -13184,6 +13514,7 @@
 	|	tok = <RETAIN>
 	|	tok = <RETURNING>
 	|	tok = <RETURNS>
+	|	tok = <ROLE>
 	|	tok = <ROW>
 //	|	tok = <ROW_COUNT>
 	|   tok = <RR>

Added: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java?rev=584738&view=auto
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java (added)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java Mon Oct 15 04:24:15 2007
@@ -0,0 +1,445 @@
+/*
+
+   Derby - Class org.apache.derbyTesting.functionTests.tests.jdbcapi.RolesTest
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+
+package org.apache.derbyTesting.functionTests.tests.lang;
+
+import java.sql.SQLException;
+import java.sql.Connection;
+import java.sql.Statement;
+import java.sql.PreparedStatement;
+import javax.sql.DataSource;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
+import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
+import org.apache.derbyTesting.junit.JDBC;
+import org.apache.derbyTesting.junit.JDBCDataSource;
+import org.apache.derbyTesting.junit.TestConfiguration;
+
+/**
+ * This JUnit tests the SQL roles feature. This feature relies on
+ * sqlAuthorization being set. Upgrade tests are not handled here.
+ *
+ * The tests are run in the cross product:
+ *
+ *    {client/server, embedded} x
+ *    {no sqlAuthorization/sqlAuthorization} x
+ *    {data base owner, other user }
+ *
+ */
+public class RolesTest extends BaseJDBCTestCase
+{
+    /* internal state */
+    private final int _authLevel;
+    private final String _user;
+    private final String _userPassword;
+    private Connection _conn;
+    private Statement _stm;
+
+    /* test execution security context: one of two below */
+    private final static int NO_SQLAUTHORIZATION=0;
+    private final static int SQLAUTHORIZATION=1;
+
+    private final static String pwSuffix = "pwSuffix";
+
+    /* SQL states */
+    private final static String sqlAuthorizationRequired = "42Z60";
+    private final static String syntaxError = "42X01";
+    // temporary until feature fully implemented:
+    private final static String notImplemented = "0A000";
+
+    /**
+     * Users used by all suites when when authLevel == SQLAUTHORIZATION.
+     * The TestConfiguration.sqlAuthorizationDecorator decorator presumes
+     * TEST_DBO as dbo, so add it to set of valid users. It uses a fresh db
+     * 'dbsqlauth', not 'wombat'.
+     */
+    private final static String[] users = {"TEST_DBO", "DonaldDuck"};
+
+    private boolean isDbo() {
+        return users[0].equals(this._user);
+    }
+
+    /**
+     * Create a new instance of RolesTest.
+     *
+     * @param name Fixture name
+     * @param authLevel authentication level with which test is run
+     * @param user Database user
+     * @param userPassword Database user's password
+     */
+
+    public RolesTest(String name, int authLevel,
+                     String user, String userPassword)
+    {
+        super(name);
+        this._authLevel = authLevel;
+        this._user = user;
+        this._userPassword = userPassword;
+    }
+
+
+    /**
+     * Construct top level suite in this JUnit test
+     *
+     * @return A suite containing embedded and client suites.
+     *         Client/server suite commented out to speed up this test as
+     *         it does not add much value given the nature of the changes
+     *         (SQL language only).
+     */
+    public static Test suite()
+    {
+        TestSuite suite = new TestSuite("RolesTest");
+
+        /* Negative syntax tests */
+        suite.addTest(negativeSyntaxSuite("suite: negative syntax, embedded"));
+
+        // suite.addTest(
+        //     TestConfiguration.clientServerDecorator(
+        //         negativeSyntaxSuite("suite: negative syntax, client")));
+
+        /* Positive tests */
+        suite.addTest(
+            positiveSuite("suite: positive, embedded"));
+
+        // suite.addTest(
+        //     TestConfiguration.clientServerDecorator(
+        //         positiveSuite("suite: positive, client")));
+
+        return suite;
+    }
+
+    /**
+     *
+     * Construct suite of tests for negative syntax
+     *
+     * @param framework Derby framework indication
+     * @return A suite containing the test cases for negative syntax
+     * incarnated for the two security levels no sqlAuthorization, and
+     * sqlAuthorization, The latter has an instance for dbo, and one
+     * for an ordinary user, so there are in all three incarnations of
+     * tests.
+     */
+    private static Test negativeSyntaxSuite(String framework)
+    {
+        Test tests[] = new Test[SQLAUTHORIZATION+1]; // one per authLevel
+
+        /* Tests running without sql authorization set.
+         */
+        TestSuite noauthSuite = new TestSuite(
+            "suite: security level=noSqlAuthorization");
+        noauthSuite.addTest(new RolesTest("testNegativeSyntax",
+                                          NO_SQLAUTHORIZATION,
+                                          null,
+                                          null));
+        tests[NO_SQLAUTHORIZATION] = noauthSuite;
+
+        /* Tests running with sql authorization set.
+         * First decorate with users, then with authentication +
+         * sqlAuthorization.
+         */
+        tests[SQLAUTHORIZATION] = wrapTest("testNegativeSyntax");
+
+
+        TestSuite suite = new TestSuite("roles:"+framework);
+        suite.addTest(tests[NO_SQLAUTHORIZATION]);
+        suite.addTest(tests[SQLAUTHORIZATION]);
+
+        return suite;
+    }
+
+
+
+    /**
+     * Wraps the negative syntax fixture in decorators to run with
+     * data base owner and one other valid user in sqlAuthorization
+     * mode.
+     */
+
+    /**
+     * Test negative syntax for roles.
+     *
+     * @throws SQLException
+     */
+    public void testNegativeSyntax() throws SQLException
+    {
+        println("testNegativeSyntax: auth=" + this._authLevel +
+                " user="+getTestConfiguration().getUserName());
+
+        _conn = getConnection();
+        _stm  = _conn.createStatement();
+
+        doStmt("create role none", // none is reserved word
+               syntaxError, syntaxError, syntaxError);
+        doStmt("create role current_role", // current_role is reserved word
+               syntaxError, syntaxError, syntaxError);
+    }
+
+    /**
+     *
+     * Construct suite of positive tests
+     *
+     * @param framework Derby framework indication
+     *
+     * @return A suite containing the positive test cases incarnated only
+     * for security level sqlAuthorization.
+     *
+     * It has one instance for dbo, and one for an ordinary user, so there
+     * are in all three incarnations of tests.
+     */
+    private static Test positiveSuite(String framework)
+    {
+        Test tests[] = new Test[SQLAUTHORIZATION+1]; // one per authLevel
+        /* Tests running without sql authorization set.
+         */
+        TestSuite noauthSuite = new TestSuite(
+            "suite: security level=noSqlAuthorization");
+        noauthSuite.addTest(new RolesTest("testPositive",
+                                          NO_SQLAUTHORIZATION,
+                                          null,
+                                          null));
+        tests[NO_SQLAUTHORIZATION] = noauthSuite;
+        /* Tests running with sql authorization set.
+         * First decorate with users, then with authentication +
+         * sqlAuthorization.
+         */
+        TestSuite suite = new TestSuite("roles:"+framework);
+        tests[SQLAUTHORIZATION] = wrapTest("testPositive");
+
+        suite.addTest(tests[NO_SQLAUTHORIZATION]);
+        suite.addTest(tests[SQLAUTHORIZATION]);
+
+        return suite;
+    }
+
+    /**
+     * Wraps in decorators to run with data base owner and one other
+     * valid user in sqlAuthorization mode.
+     *
+     * @param testName test to wrap
+     */
+    private static Test wrapTest(String testName)
+    {
+        // add decorator for different users authenticated
+        TestSuite usersSuite =
+            new TestSuite("suite: security level=sqlAuthorization");
+
+        // First decorate with users, then with authorization
+        // decorator
+        for (int userNo = 0; userNo < users.length; userNo++) {
+            usersSuite.addTest
+                (TestConfiguration.changeUserDecorator
+                 (new RolesTest(testName,
+                                SQLAUTHORIZATION,
+                                users[userNo],
+                                users[userNo].concat(pwSuffix)),
+                  users[userNo],
+                  users[userNo].concat(pwSuffix)));
+        }
+
+        return TestConfiguration.sqlAuthorizationDecorator(
+            DatabasePropertyTestSetup.builtinAuthentication(
+                usersSuite, users, pwSuffix));
+    }
+
+    /**
+     * Positive tests for roles (well, positive for dbo at least!)
+     *
+     * @throws SQLException
+     */
+    public void testPositive() throws SQLException
+    {
+        println("testPositive: auth=" + this._authLevel +
+                " user="+getTestConfiguration().getUserName());
+
+        _conn = getConnection();
+        _stm  = _conn.createStatement();
+
+        // create
+        doStmt("create role foo",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("create role bar",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("create role role", // role is not reserved word
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("create role trigger",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("create role \"NONE\"", // quoted role id should work
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+
+        // grant
+        doStmt("grant foo to authid", // authid: user or role
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("grant foo, role, bar to authid1, authid2, authid3",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+
+        // grant: parser look-ahead tests to discern grant role from
+        // grant privilege
+        doStmt("grant trigger to authid",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("grant trigger, foo to authid",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("grant trigger, foo to public",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+
+
+        // set
+        doStmt("set role foo",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("set role none",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doDynamicSetRole(_conn);
+
+        // revoke
+
+        doStmt("revoke foo from authid", // authid: user or role
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("revoke foo, role, bar from authid1, authid2, authid3",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+
+        // revoke: parser look-ahead tests to discern revoke role from
+        // revoke privilege
+        doStmt("revoke trigger from authid",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("revoke trigger, foo from authid",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("revoke trigger, foo from public",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+
+        // drop
+        doStmt("drop role foo",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("drop role role",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("drop role trigger",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+        doStmt("drop role \"NONE\"",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+
+        // current_role
+        doStmt("values current_role",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+
+        // column default current_role
+        doStmt("create table foo(str varchar(128) default current_role)",
+               sqlAuthorizationRequired, notImplemented, notImplemented);
+    }
+
+    // Minion to analyze outcome. If state string is empty, we expect success
+    // for that combination of authentication level and user (dbo or not).
+    private void doStmt(String stmt,
+                        String noAuthState,
+                        String authDboState,
+                        String authNotDboState) {
+        try {
+            _stm.execute(stmt);
+            if (_authLevel == NO_SQLAUTHORIZATION) {
+                if (noAuthState != null) {
+                    fail("exception " + noAuthState + " expected: (" + stmt);
+                }
+            } else { // SQLAUTHORIZATION
+                if (isDbo()) {
+                    if (authDboState != null) {
+                        fail("exception " + noAuthState + " expected: (" +
+                             stmt);
+                    }
+                } else {
+                    if (authNotDboState != null) {
+                        fail("exception " + noAuthState + " expected: (" +
+                             stmt);
+                    }
+                }
+            }
+        } catch (SQLException e) {
+            if (_authLevel == NO_SQLAUTHORIZATION) {
+                if (noAuthState == null) {
+                    fail("stmt " + stmt + " failed with exception " +
+                         e.getSQLState());
+                } else {
+                    assertSQLState("Stmt " + stmt, noAuthState, e);
+                }
+
+            } else { // SQLAUTHORIZATION
+                if (isDbo()) {
+                    if (authDboState == null) {
+                        fail("stmt " + stmt + " failed with exception " +
+                             e.getSQLState());
+                    } else {
+                        assertSQLState("Stmt " + stmt, authDboState, e);
+                    }
+                } else {
+                    if (authNotDboState == null) {
+                        fail("stmt " + stmt + " failed with exception " +
+                             e.getSQLState());
+                    } else {
+                        assertSQLState("Stmt " + stmt, authNotDboState, e);
+                    }
+                }
+            }
+        }
+    }
+
+
+    private void doDynamicSetRole(Connection conn) {
+        PreparedStatement pstmt = null;
+
+        try {
+            pstmt = conn.prepareStatement("set role ?");
+
+            if (_authLevel == NO_SQLAUTHORIZATION) {
+                fail("set role ? should have failed; no sqlAuthorization");
+            }
+        } catch (SQLException e) {
+             if (_authLevel == NO_SQLAUTHORIZATION) {
+                 assertSQLState(sqlAuthorizationRequired, e);
+                 return;
+             } else {
+                 // fail("prepare of set role ? failed:" + e);
+                 assertSQLState(notImplemented, e);
+                 return;
+             }
+        }
+
+        try {
+            pstmt.setString(1, "foo");
+            int rowcnt = pstmt.executeUpdate();
+            assertEquals(rowcnt, 0, "rowcount from set role ? not 0");
+        } catch (SQLException e) {
+            assertSQLState(notImplemented, e);
+        }
+
+
+        try {
+            pstmt.setString(1, "\"NONE\"");
+            int rowcnt = pstmt.executeUpdate();
+            assertEquals(rowcnt, 0, "rowcount from set role ? not 0");
+        } catch (SQLException e) {
+            assertSQLState(notImplemented, e);
+        }
+    }
+
+
+    private void assertEquals(int a, int b, String txt) {
+        if (a!=b) {
+            fail(txt);
+        }
+    }
+}

Propchange: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/RolesTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java?rev=584738&r1=584737&r2=584738&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/_Suite.java Mon Oct 15 04:24:15 2007
@@ -69,6 +69,7 @@
 		suite.addTest(LangScripts.suite());
         suite.addTest(MathTrigFunctionsTest.suite());
         suite.addTest(PrepareExecuteDDL.suite());
+        suite.addTest(RolesTest.suite());
         suite.addTest(RoutineSecurityTest.suite());
         suite.addTest(RoutineTest.suite());
         suite.addTest(SQLAuthorizationPropTest.suite());