You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ee...@apache.org on 2011/01/27 22:57:20 UTC

svn commit: r1064326 - in /cassandra/trunk: src/java/org/apache/cassandra/cql/ test/system/

Author: eevans
Date: Thu Jan 27 21:57:20 2011
New Revision: 1064326

URL: http://svn.apache.org/viewvc?rev=1064326&view=rev
Log:
CQL: keyspace creation

Patch by eevans for CASSANDRA-1709

Added:
    cassandra/trunk/src/java/org/apache/cassandra/cql/CreateKeyspaceStatement.java
Modified:
    cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g
    cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java
    cassandra/trunk/src/java/org/apache/cassandra/cql/StatementType.java
    cassandra/trunk/test/system/test_cql.py

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g?rev=1064326&r1=1064325&r2=1064326&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/Cql.g Thu Jan 27 21:57:20 2011
@@ -46,6 +46,7 @@ query returns [CQLStatement stmnt]
     | useStatement      { $stmnt = new CQLStatement(StatementType.USE, $useStatement.keyspace); }
     | truncateStatement { $stmnt = new CQLStatement(StatementType.TRUNCATE, $truncateStatement.cfam); }
     | deleteStatement   { $stmnt = new CQLStatement(StatementType.DELETE, $deleteStatement.expr); }
+    | createKeyspaceStatement { $stmnt = new CQLStatement(StatementType.CREATE_KEYSPACE, $createKeyspaceStatement.expr); }
     ;
 
 // USE <KEYSPACE>;
@@ -161,6 +162,20 @@ deleteStatement returns [DeleteStatement
       }
     ;
 
+/** CREATE KEYSPACE <KEYSPACE> WITH attr1 = value1 AND attr2 = value2; */
+createKeyspaceStatement returns [CreateKeyspaceStatement expr]
+    : {
+          Map<String, String> attrs = new HashMap<String, String>();
+      }
+      K_CREATE K_KEYSPACE keyspace=IDENT
+          K_WITH  a1=( COMPIDENT | IDENT ) '=' v1=( STRING_LITERAL | INTEGER ) { attrs.put($a1.text, $v1.text); }
+          ( K_AND aN=( COMPIDENT | IDENT ) '=' vN=( STRING_LITERAL | INTEGER ) { attrs.put($aN.text, $vN.text); } )*
+          endStmnt
+      {
+          return new CreateKeyspaceStatement($keyspace.text, attrs);
+      }
+    ;
+
 // TODO: date/time, utf8
 term returns [Term item]
     : ( t=STRING_LITERAL | t=LONG )
@@ -245,6 +260,8 @@ K_BATCH:       B A T C H;
 K_TRUNCATE:    T R U N C A T E;
 K_DELETE:      D E L E T E;
 K_IN:          I N;
+K_CREATE:      C R E A T E;
+K_KEYSPACE:    K E Y S P A C E;
 
 // Case-insensitive alpha characters
 fragment A: ('a'|'A');
@@ -304,6 +321,10 @@ IDENT
     : LETTER (LETTER | DIGIT | '_')*
     ;
     
+COMPIDENT
+    : IDENT ( ':' IDENT)*
+    ;
+    
 WS
     : (' ' | '\t' | '\n' | '\r')+ { $channel = HIDDEN; }
     ;

Added: cassandra/trunk/src/java/org/apache/cassandra/cql/CreateKeyspaceStatement.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/CreateKeyspaceStatement.java?rev=1064326&view=auto
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/CreateKeyspaceStatement.java (added)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/CreateKeyspaceStatement.java Thu Jan 27 21:57:20 2011
@@ -0,0 +1,82 @@
+package org.apache.cassandra.cql;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.cassandra.thrift.InvalidRequestException;
+
+/** A <code>CREATE KEYSPACE</code> statement parsed from a CQL query. */
+public class CreateKeyspaceStatement
+{   
+    private final String name;
+    private final Map<String, String> attrs;
+    private String strategyClass;
+    private int replicationFactor;
+    private Map<String, String> strategyOptions = new HashMap<String, String>();
+    
+    /**
+     * Creates a new <code>CreateKeyspaceStatement</code> instance for a given
+     * keyspace name and keyword arguments.
+     *  
+     * @param name the name of the keyspace to create
+     * @param attrs map of the raw keyword arguments that followed the <code>WITH</code> keyword.
+     */
+    public CreateKeyspaceStatement(String name, Map<String, String> attrs)
+    {
+        this.name = name;
+        this.attrs = attrs;
+    }
+    
+    /**
+     * The <code>CqlParser</code> only goes as far as extracting the keyword arguments
+     * from these statements, so this method is responsible for processing and
+     * validating, and must be called prior to access.
+     * 
+     * @throws InvalidRequestException if arguments are missing or unacceptable
+     */
+    public void validate() throws InvalidRequestException
+    {   
+        // required
+        if (!attrs.containsKey("strategy_class"))
+            throw new InvalidRequestException("missing required argument \"strategy_class\"");
+        strategyClass = attrs.get("strategy_class");
+        
+        // required
+        if (!attrs.containsKey("replication_factor"))
+            throw new InvalidRequestException("missing required argument \"replication_factor\"");
+        
+        try
+        {
+            replicationFactor = Integer.parseInt(attrs.get("replication_factor"));
+        }
+        catch (NumberFormatException e)
+        {
+            throw new InvalidRequestException(String.format("\"%s\" is not valid for replication_factor",
+                                                            attrs.get("replication_factor")));
+        }
+        
+        // optional
+        for (String key : attrs.keySet())
+            if ((key.contains(":")) && (key.startsWith("strategy_options")))
+                strategyOptions.put(key.split(":")[1], attrs.get(key));
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public String getStrategyClass()
+    {
+        return strategyClass;
+    }
+    
+    public int getReplicationFactor()
+    {
+        return replicationFactor;
+    }
+    
+    public Map<String, String> getStrategyOptions()
+    {
+        return strategyOptions;
+    }
+}

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java?rev=1064326&r1=1064325&r2=1064326&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/QueryProcessor.java Thu Jan 27 21:57:20 2011
@@ -24,17 +24,27 @@ package org.apache.cassandra.cql;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
 import java.util.concurrent.TimeoutException;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.antlr.runtime.*;
+import org.apache.cassandra.concurrent.Stage;
+import org.apache.cassandra.concurrent.StageManager;
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.config.KSMetaData;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.filter.QueryPath;
+import org.apache.cassandra.db.migration.AddKeyspace;
+import org.apache.cassandra.db.migration.Migration;
 import org.apache.cassandra.dht.AbstractBounds;
 import org.apache.cassandra.dht.Bounds;
 import org.apache.cassandra.dht.IPartitioner;
+import org.apache.cassandra.locator.AbstractReplicationStrategy;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.StorageProxy;
 import org.apache.cassandra.service.StorageService;
@@ -267,6 +277,44 @@ public class QueryProcessor
             throw new InvalidRequestException("No indexed columns present in by-columns clause with \"equals\" operator");
         }
     }
+    
+    // Copypasta from o.a.c.thrift.CassandraDaemon
+    private static void applyMigrationOnStage(final Migration m) throws InvalidRequestException
+    {
+        Future f = StageManager.getStage(Stage.MIGRATION).submit(new Callable()
+        {
+            public Object call() throws Exception
+            {
+                m.apply();
+                m.announce();
+                return null;
+            }
+        });
+        try
+        {
+            f.get();
+        }
+        catch (InterruptedException e)
+        {
+            throw new RuntimeException(e);
+        }
+        catch (ExecutionException e)
+        {
+            // this means call() threw an exception. deal with it directly.
+            if (e.getCause() != null)
+            {
+                InvalidRequestException ex = new InvalidRequestException(e.getCause().getMessage());
+                ex.initCause(e.getCause());
+                throw ex;
+            }
+            else
+            {
+                InvalidRequestException ex = new InvalidRequestException(e.getMessage());
+                ex.initCause(e);
+                throw ex;
+            }
+        }
+    }
 
     public static CqlResult process(String queryString, ClientState clientState)
     throws RecognitionException, UnavailableException, InvalidRequestException, TimedOutException
@@ -426,6 +474,35 @@ public class QueryProcessor
                 
                 avroResult.type = CqlResultType.VOID;
                 return avroResult;
+                
+            case CREATE_KEYSPACE:
+                CreateKeyspaceStatement create = (CreateKeyspaceStatement)statement.statement;
+                create.validate();
+                
+                try
+                {
+                    KSMetaData ksm = new KSMetaData(create.getName(),
+                                                    AbstractReplicationStrategy.getClass(create.getStrategyClass()),
+                                                    create.getStrategyOptions(),
+                                                    create.getReplicationFactor());
+                    applyMigrationOnStage(new AddKeyspace(ksm));
+                }
+                catch (ConfigurationException e)
+                {
+                    InvalidRequestException ex = new InvalidRequestException(e.getMessage());
+                    ex.initCause(e);
+                    throw ex;
+                }
+                catch (IOException e)
+                {
+                    InvalidRequestException ex = new InvalidRequestException(e.getMessage());
+                    ex.initCause(e);
+                    throw ex;
+                }
+                
+                avroResult.type = CqlResultType.VOID;
+                return avroResult;
+                
         }
         
         return null;    // We should never get here.

Modified: cassandra/trunk/src/java/org/apache/cassandra/cql/StatementType.java
URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/cql/StatementType.java?rev=1064326&r1=1064325&r2=1064326&view=diff
==============================================================================
--- cassandra/trunk/src/java/org/apache/cassandra/cql/StatementType.java (original)
+++ cassandra/trunk/src/java/org/apache/cassandra/cql/StatementType.java Thu Jan 27 21:57:20 2011
@@ -22,5 +22,5 @@ package org.apache.cassandra.cql;
 
 public enum StatementType
 {
-    SELECT, UPDATE, BATCH_UPDATE, USE, TRUNCATE, DELETE;
+    SELECT, UPDATE, BATCH_UPDATE, USE, TRUNCATE, DELETE, CREATE_KEYSPACE;
 }

Modified: cassandra/trunk/test/system/test_cql.py
URL: http://svn.apache.org/viewvc/cassandra/trunk/test/system/test_cql.py?rev=1064326&r1=1064325&r2=1064326&view=diff
==============================================================================
--- cassandra/trunk/test/system/test_cql.py (original)
+++ cassandra/trunk/test/system/test_cql.py Thu Jan 27 21:57:20 2011
@@ -7,6 +7,7 @@ sys.path.append(join(abspath(dirname(__f
 from cql import Connection
 from cql.errors import CQLException
 from . import ThriftTester
+from . import thrift_client     # TODO: temporary
 
 def assert_raises(exception, method, *args):
     try:
@@ -207,4 +208,19 @@ class TestCql(ThriftTester):
         conn.execute('DELETE FROM Standard1 WHERE KEY = "kd"')
         r = conn.execute('SELECT "cd1", "col" FROM Standard1 WHERE KEY = "kd"')
         assert len(r[0].columns) == 0
+        
+    def test_create_keyspace(self):
+        "create a new keyspace"
+        init().execute("""
+        CREATE KEYSPACE TestKeyspace42 WITH strategy_options:DC1 = 1"
+            AND strategy_class = "SimpleStrategy" AND replication_factor = 3
+        """)
+        
+        # TODO: temporary (until this can be done with CQL).
+        ksdef = thrift_client.describe_keyspace("TestKeyspace42")
+        
+        assert ksdef.replication_factor == 3
+        strategy_class = "org.apache.cassandra.locator.SimpleStrategy"
+        assert ksdef.strategy_class == strategy_class
+        assert ksdef.strategy_options['DC1'] == "1"