add support for insert, delete in cql BATCH

patch by Pavel Yaskevich; reviewed by jbellis for CASSANDRA-2537

git-svn-id: https://svn.apache.org/repos/asf/cassandra/trunk@1096771 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jonathan Ellis 2011-04-26 14:53:53 +00:00
parent 27cf0a9b74
commit 299b7be025
10 changed files with 400 additions and 93 deletions

View File

@ -3,6 +3,7 @@
* Remove checking all column families on startup for compaction candidates (CASSANDRA-2444)
* validate CQL create keyspace options (CASSANDRA-2525)
* fix nodetool setcompactionthroughput (CASSANDRA-2550)
* add support for insert, delete in cql BATCH (CASSANDRA-2537)
0.8.0-beta1

View File

@ -99,19 +99,6 @@ UPDATE ... SET name1 = value1, name2 = value2 WHERE KEY = keyname;
Rows are created or updated by supplying column names and values in term assignment format. Multiple columns can be set by separating the name/value pairs using commas. Each update statement requires exactly one key to be specified using a WHERE clause and the @KEY@ keyword.
Additionally, it is also possible to send multiple UPDATES to a node at once using a batch syntax:
bc.
BEGIN BATCH [USING <CONSISTENCY>]
UPDATE CF1 SET name1 = value1, name2 = value2 WHERE KEY = keyname1;
UPDATE CF1 SET name3 = value3 WHERE KEY = keyname2;
UPDATE CF2 SET name4 = value4, name5 = value5 WHERE KEY = keyname3;
APPLY BATCH
When batching UPDATEs, a single consistency level is used for the entire batch, it appears after the @BEGIN BATCH@ statement, and uses the standard "consistency level specification":#consistency. Batch UPDATEs default to @CONSISTENCY.ONE@ when left unspecified.
_NOTE: While there are no isolation guarantees, @UPDATE@ queries are atomic within a give record._
h2. DELETE
_Synopsis:_
@ -150,6 +137,30 @@ UPDATE ... WHERE KEY = keyname1
UPDATE ... WHERE KEY IN (keyname1, keyname2)
The @WHERE@ clause is used to determine which row(s) a @DELETE@ applies to. The first form allows the specification of a single keyname using the @KEY@ keyword and the @=@ operator. The second form allows a list of keyname terms to be specified using the @IN@ notation and a parenthesized list of comma-delimited keyname terms.
h2. BATCH
_Synopsis:_
bc.
BATCH BEGIN BATCH [USING CONSISTENCY <LEVEL>]
INSERT or UPDATE or DELETE statements separated by semicolon or "end of line"
APPLY BATCH
A single consistency level is used for the entire batch, it appears after the @BEGIN BATCH@ statement, and uses the standard "consistency level specification":#consistency. Batch default to @CONSISTENCY.ONE@ when left unspecified.
_NOTE: While there are no isolation guarantees, @UPDATE@ queries are atomic within a give record._
_Example:_
bc.
BEGIN BATCH USING CONSISTENCY QUORUM
INSERT INTO users (KEY, password, name) VALUES ('user2', 'ch@ngem3b', 'second user')
UPDATE users SET password = 'ps22dhds' WHERE KEY = 'user2'
INSERT INTO users (KEY, password) VALUES ('user3', 'ch@ngem3c')
DELETE name FROM users WHERE key = 'user2'
INSERT INTO users (KEY, password, name) VALUES ('user4', 'ch@ngem3c', 'Andrew')
APPLY BATCH
h2. TRUNCATE

View File

@ -0,0 +1,74 @@
/*
*
* 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.cassandra.cql;
import org.apache.cassandra.db.RowMutation;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.thrift.ConsistencyLevel;
import java.util.List;
public abstract class AbstractModification
{
public static final ConsistencyLevel defaultConsistency = ConsistencyLevel.ONE;
protected final String columnFamily;
protected final ConsistencyLevel cLevel;
public AbstractModification(String columnFamily, ConsistencyLevel cLevel)
{
this.columnFamily = columnFamily;
this.cLevel = cLevel;
}
public String getColumnFamily()
{
return columnFamily;
}
public ConsistencyLevel getConsistencyLevel()
{
return (cLevel != null) ? cLevel : defaultConsistency;
}
/**
* True if an explicit consistency level was parsed from the statement.
*
* @return true if a consistency was parsed, false otherwise.
*/
public boolean isSetConsistencyLevel()
{
return cLevel != null;
}
/**
* Convert statement into a list of mutations to apply on the server
*
* @param keyspace The working keyspace
* @param clientState current client status
*
* @return list of the mutations
*
* @throws org.apache.cassandra.thrift.InvalidRequestException on the wrong request
*/
public abstract List<RowMutation> prepareRowMutations(String keyspace, ClientState clientState)
throws org.apache.cassandra.thrift.InvalidRequestException;
}

View File

@ -0,0 +1,84 @@
/*
*
* 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.cassandra.cql;
import java.util.LinkedList;
import java.util.List;
import org.apache.cassandra.db.RowMutation;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.thrift.ConsistencyLevel;
import org.apache.cassandra.thrift.InvalidRequestException;
/**
* A <code>BATCH</code> statement parsed from a CQL query.
*
*/
public class BatchStatement
{
// statements to execute
protected final List<AbstractModification> statements;
// global consistency level
protected final ConsistencyLevel consistency;
/**
* Creates a new BatchStatement from a list of statements and a
* Thrift consistency level.
*
* @param statements a list of UpdateStatements
* @param level Thrift consistency level enum
*/
public BatchStatement(List<AbstractModification> statements, ConsistencyLevel level)
{
this.statements = statements;
consistency = level;
}
public List<AbstractModification> getStatements()
{
return statements;
}
public ConsistencyLevel getConsistencyLevel()
{
return consistency;
}
public List<RowMutation> getMutations(String keyspace, ClientState clientState) throws InvalidRequestException
{
List<RowMutation> batch = new LinkedList<RowMutation>();
for (AbstractModification statement : statements)
{
batch.addAll(statement.prepareRowMutations(keyspace, clientState));
}
return batch;
}
public String toString()
{
return String.format("BatchStatement(statements=%s, consistency=%s)", statements, consistency);
}
}

View File

@ -102,12 +102,12 @@ options {
query returns [CQLStatement stmnt]
: selectStatement { $stmnt = new CQLStatement(StatementType.SELECT, $selectStatement.expr); }
| insertStatement { $stmnt = new CQLStatement(StatementType.INSERT, $insertStatement.expr); }
| insertStatement endStmnt { $stmnt = new CQLStatement(StatementType.INSERT, $insertStatement.expr); }
| updateStatement endStmnt { $stmnt = new CQLStatement(StatementType.UPDATE, $updateStatement.expr); }
| batchUpdateStatement { $stmnt = new CQLStatement(StatementType.BATCH_UPDATE, $batchUpdateStatement.expr); }
| batchStatement { $stmnt = new CQLStatement(StatementType.BATCH, $batchStatement.expr); }
| useStatement { $stmnt = new CQLStatement(StatementType.USE, $useStatement.keyspace); }
| truncateStatement { $stmnt = new CQLStatement(StatementType.TRUNCATE, $truncateStatement.cfam); }
| deleteStatement { $stmnt = new CQLStatement(StatementType.DELETE, $deleteStatement.expr); }
| deleteStatement endStmnt { $stmnt = new CQLStatement(StatementType.DELETE, $deleteStatement.expr); }
| createKeyspaceStatement { $stmnt = new CQLStatement(StatementType.CREATE_KEYSPACE, $createKeyspaceStatement.expr); }
| createColumnFamilyStatement { $stmnt = new CQLStatement(StatementType.CREATE_COLUMNFAMILY, $createColumnFamilyStatement.expr); }
| createIndexStatement { $stmnt = new CQLStatement(StatementType.CREATE_INDEX, $createIndexStatement.expr); }
@ -191,7 +191,7 @@ whereClause returns [WhereClause clause]
*/
insertStatement returns [UpdateStatement expr]
: {
ConsistencyLevel cLevel = ConsistencyLevel.ONE;
ConsistencyLevel cLevel = null;
Map<Term, Term> columns = new HashMap<Term, Term>();
List<Term> columnNames = new ArrayList<Term>();
@ -202,32 +202,54 @@ insertStatement returns [UpdateStatement expr]
K_VALUES
'(' key=term ( ',' column_value=term { columnValues.add($column_value.item); })+ ')'
( K_USING K_CONSISTENCY K_LEVEL { cLevel = ConsistencyLevel.valueOf($K_LEVEL.text); } )?
endStmnt
{
return new UpdateStatement($columnFamily.text, cLevel, columnNames, columnValues, key);
}
;
/**
* BEGIN BATCH [USING CONSISTENCY.<LVL>]
* UPDATE <CF> SET name1 = value1 WHERE KEY = keyname1;
* UPDATE <CF> SET name2 = value2 WHERE KEY = keyname2;
* UPDATE <CF> SET name3 = value3 WHERE KEY = keyname3;
* BEGIN BATCH [USING CONSISTENCY <LVL>]
* UPDATE <CF> SET name1 = value1 WHERE KEY = keyname1;
* UPDATE <CF> SET name2 = value2 WHERE KEY = keyname2;
* UPDATE <CF> SET name3 = value3 WHERE KEY = keyname3;
* ...
* APPLY BATCH
*
* OR
*
* BEGIN BATCH [USING CONSISTENCY <LVL>]
* INSERT INTO <CF> (KEY, <name>) VALUES ('<key>', '<value>');
* INSERT INTO <CF> (KEY, <name>) VALUES ('<key>', '<value>');
* ...
* APPLY BATCH
*
* OR
*
* BEGIN BATCH [USING CONSISTENCY <LVL>]
* DELETE name1, name2 FROM <CF> WHERE key = <key>
* DELETE name3, name4 FROM <CF> WHERE key = <key>
* ...
* APPLY BATCH
*/
batchUpdateStatement returns [BatchUpdateStatement expr]
batchStatement returns [BatchStatement expr]
: {
ConsistencyLevel cLevel = ConsistencyLevel.ONE;
List<UpdateStatement> updates = new ArrayList<UpdateStatement>();
List<AbstractModification> statements = new ArrayList<AbstractModification>();
}
K_BEGIN K_BATCH ( K_USING K_CONSISTENCY K_LEVEL { cLevel = ConsistencyLevel.valueOf($K_LEVEL.text); } )?
u1=updateStatement ';'? { updates.add(u1); } ( uN=updateStatement ';'? { updates.add(uN); } )*
K_APPLY K_BATCH EOF
s1=batchStatementObjective ';'? { statements.add(s1); } ( sN=batchStatementObjective ';'? { statements.add(sN); } )*
K_APPLY K_BATCH endStmnt
{
return new BatchUpdateStatement(updates, cLevel);
return new BatchStatement(statements, cLevel);
}
;
batchStatementObjective returns [AbstractModification statement]
: i=insertStatement { $statement = i; }
| u=updateStatement { $statement = u; }
| d=deleteStatement { $statement = d; }
;
/**
* UPDATE
* <CF>
@ -265,7 +287,7 @@ updateStatement returns [UpdateStatement expr]
*/
deleteStatement returns [DeleteStatement expr]
: {
ConsistencyLevel cLevel = ConsistencyLevel.ONE;
ConsistencyLevel cLevel = null;
List<Term> keyList = null;
List<Term> columnsList = Collections.emptyList();
}
@ -274,7 +296,7 @@ deleteStatement returns [DeleteStatement expr]
K_FROM columnFamily=( IDENT | STRING_LITERAL | INTEGER ) ( K_USING K_CONSISTENCY K_LEVEL )?
K_WHERE ( K_KEY '=' key=term { keyList = Collections.singletonList(key); }
| K_KEY K_IN '(' keys=termList { keyList = $keys.items; } ')'
)? endStmnt
)?
{
return new DeleteStatement(columnsList, $columnFamily.text, cLevel, keyList);
}

View File

@ -20,26 +20,37 @@
*/
package org.apache.cassandra.cql;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.RowMutation;
import org.apache.cassandra.db.filter.QueryPath;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.thrift.ConsistencyLevel;
import org.apache.cassandra.thrift.InvalidRequestException;
import static org.apache.cassandra.thrift.ThriftValidation.validateColumnFamily;
import static org.apache.cassandra.cql.QueryProcessor.validateColumnName;
/**
* A <code>DELETE</code> parsed from a CQL query statement.
*
*/
public class DeleteStatement
public class DeleteStatement extends AbstractModification
{
private List<Term> columns;
private String columnFamily;
private ConsistencyLevel cLevel;
private List<Term> keys;
public DeleteStatement(List<Term> columns, String columnFamily, ConsistencyLevel cLevel, List<Term> keys)
{
super(columnFamily, cLevel);
this.columns = columns;
this.columnFamily = columnFamily;
this.cLevel = cLevel;
this.keys = keys;
}
@ -48,21 +59,44 @@ public class DeleteStatement
return columns;
}
public String getColumnFamily()
{
return columnFamily;
}
public ConsistencyLevel getConsistencyLevel()
{
return cLevel;
}
public List<Term> getKeys()
{
return keys;
}
/** {@inheritDoc} */
public List<RowMutation> prepareRowMutations(String keyspace, ClientState clientState) throws InvalidRequestException
{
clientState.hasColumnFamilyAccess(columnFamily, Permission.WRITE);
CFMetaData metadata = validateColumnFamily(keyspace, columnFamily, false);
AbstractType comparator = metadata.getComparatorFor(null);
AbstractType<?> keyType = DatabaseDescriptor.getCFMetaData(keyspace, columnFamily).getKeyValidator();
List<RowMutation> rowMutations = new ArrayList<RowMutation>();
for (Term key : keys)
{
RowMutation rm = new RowMutation(keyspace, key.getByteBuffer(keyType));
if (columns.size() < 1) // No columns, delete the row
rm.delete(new QueryPath(columnFamily), System.currentTimeMillis());
else // Delete specific columns
{
for (Term column : columns)
{
ByteBuffer columnName = column.getByteBuffer(comparator);
validateColumnName(columnName);
rm.delete(new QueryPath(columnFamily, null, columnName), System.currentTimeMillis());
}
}
rowMutations.add(rm);
}
return rowMutations;
}
public String toString()
{
return String.format("DeleteStatement(columns=%s, columnFamily=%s, consistency=%s keys=%s)",

View File

@ -367,7 +367,7 @@ public class QueryProcessor
}
}
private static void validateKey(ByteBuffer key) throws InvalidRequestException
public static void validateKey(ByteBuffer key) throws InvalidRequestException
{
if (key == null || key.remaining() == 0)
{
@ -396,13 +396,13 @@ public class QueryProcessor
}
}
private static void validateColumnName(ByteBuffer column)
public static void validateColumnName(ByteBuffer column)
throws InvalidRequestException
{
validateColumnNames(Arrays.asList(column));
}
private static void validateColumn(CFMetaData metadata, ByteBuffer name, ByteBuffer value)
public static void validateColumn(CFMetaData metadata, ByteBuffer name, ByteBuffer value)
throws InvalidRequestException
{
validateColumnName(name);
@ -543,15 +543,27 @@ public class QueryProcessor
result.type = CqlResultType.VOID;
return result;
case BATCH_UPDATE:
BatchUpdateStatement batch = (BatchUpdateStatement)statement.statement;
for (UpdateStatement up : batch.getUpdates())
case BATCH:
BatchStatement batch = (BatchStatement) statement.statement;
for (AbstractModification up : batch.getStatements())
if (up.isSetConsistencyLevel())
throw new InvalidRequestException(
"Consistency level must be set on the BATCH, not individual UPDATE statements");
batchUpdate(clientState, batch.getUpdates(), batch.getConsistencyLevel());
"Consistency level must be set on the BATCH, not individual statements");
try
{
StorageProxy.mutate(batch.getMutations(keyspace, clientState), batch.getConsistencyLevel());
}
catch (org.apache.cassandra.thrift.UnavailableException e)
{
throw new UnavailableException();
}
catch (TimeoutException e)
{
throw new TimedOutException();
}
result.type = CqlResultType.VOID;
return result;
@ -583,34 +595,10 @@ public class QueryProcessor
case DELETE:
DeleteStatement delete = (DeleteStatement)statement.statement;
clientState.hasColumnFamilyAccess(delete.getColumnFamily(), Permission.WRITE);
metadata = validateColumnFamily(keyspace, delete.getColumnFamily(), false);
comparator = metadata.getComparatorFor(null);
AbstractType<?> keyType = DatabaseDescriptor.getCFMetaData(keyspace,
delete.getColumnFamily()).getKeyValidator();
List<RowMutation> rowMutations = new ArrayList<RowMutation>();
for (Term key : delete.getKeys())
{
RowMutation rm = new RowMutation(keyspace, key.getByteBuffer(keyType));
if (delete.getColumns().size() < 1) // No columns, delete the row
rm.delete(new QueryPath(delete.getColumnFamily()), System.currentTimeMillis());
else // Delete specific columns
{
for (Term column : delete.getColumns())
{
ByteBuffer columnName = column.getByteBuffer(comparator);
validateColumnName(columnName);
rm.delete(new QueryPath(delete.getColumnFamily(), null, columnName),
System.currentTimeMillis());
}
}
rowMutations.add(rm);
}
try
{
StorageProxy.mutate(rowMutations, delete.getConsistencyLevel());
StorageProxy.mutate(delete.prepareRowMutations(keyspace, clientState), delete.getConsistencyLevel());
}
catch (TimeoutException e)
{

View File

@ -24,7 +24,7 @@ import java.util.EnumSet;
public enum StatementType
{
SELECT, INSERT, UPDATE, BATCH_UPDATE, USE, TRUNCATE, DELETE, CREATE_KEYSPACE, CREATE_COLUMNFAMILY, CREATE_INDEX,
SELECT, INSERT, UPDATE, BATCH, USE, TRUNCATE, DELETE, CREATE_KEYSPACE, CREATE_COLUMNFAMILY, CREATE_INDEX,
DROP_KEYSPACE, DROP_COLUMNFAMILY;
// Statement types that don't require a keyspace to be set.

View File

@ -21,24 +21,29 @@
package org.apache.cassandra.cql;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.RowMutation;
import org.apache.cassandra.db.filter.QueryPath;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.thrift.ConsistencyLevel;
import org.apache.cassandra.thrift.InvalidRequestException;
import static org.apache.cassandra.cql.QueryProcessor.validateKey;
import static org.apache.cassandra.cql.QueryProcessor.validateColumn;
import static org.apache.cassandra.thrift.ThriftValidation.validateColumnFamily;
/**
* An <code>UPDATE</code> statement parsed from a CQL query statement.
*
*/
public class UpdateStatement
public class UpdateStatement extends AbstractModification
{
public static final ConsistencyLevel defaultConsistency = ConsistencyLevel.ONE;
private String columnFamily;
private ConsistencyLevel cLevel = null;
private Map<Term, Term> columns;
private List<Term> columnNames, columnValues;
private Term key;
@ -54,8 +59,8 @@ public class UpdateStatement
*/
public UpdateStatement(String columnFamily, ConsistencyLevel cLevel, Map<Term, Term> columns, Term key)
{
this.columnFamily = columnFamily;
this.cLevel = cLevel;
super(columnFamily, cLevel);
this.columns = columns;
this.key = key;
}
@ -86,8 +91,8 @@ public class UpdateStatement
*/
public UpdateStatement(String columnFamily, ConsistencyLevel cLevel, List<Term> columnNames, List<Term> columnValues, Term key)
{
this.columnFamily = columnFamily;
this.cLevel = cLevel;
super(columnFamily, cLevel);
this.columnNames = columnNames;
this.columnValues = columnValues;
this.key = key;
@ -114,6 +119,37 @@ public class UpdateStatement
return (cLevel != null);
}
/** {@inheritDoc} */
public List<RowMutation> prepareRowMutations(String keyspace, ClientState clientState) throws InvalidRequestException
{
List<String> cfamsSeen = new ArrayList<String>();
CFMetaData metadata = validateColumnFamily(keyspace, columnFamily, false);
// Avoid unnecessary authorizations.
if (!(cfamsSeen.contains(columnFamily)))
{
clientState.hasColumnFamilyAccess(columnFamily, Permission.WRITE);
cfamsSeen.add(columnFamily);
}
ByteBuffer key = this.key.getByteBuffer(getKeyType(keyspace));
validateKey(key);
AbstractType<?> comparator = getComparator(keyspace);
RowMutation rm = new RowMutation(keyspace, key);
for (Map.Entry<Term, Term> column : getColumns().entrySet())
{
ByteBuffer colName = column.getKey().getByteBuffer(comparator);
ByteBuffer colValue = column.getValue().getByteBuffer(getValueValidator(keyspace, colName));
validateColumn(metadata, colName, colValue);
rm.add(new QueryPath(columnFamily, null, colName), colValue, System.currentTimeMillis());
}
return Arrays.asList(rm);
}
public String getColumnFamily()
{
return columnFamily;

View File

@ -699,3 +699,60 @@ class TestCql(ThriftTester):
r = cursor.fetchone()
assert r[1] == "some_value", \
"unrecognized value '%s'" % r[1]
def test_batch_with_mixed_statements(self):
"handle BATCH with INSERT/UPDATE/DELETE statements mixed in it"
cursor = init()
cursor.compression = 'NONE'
cursor.execute("""
BEGIN BATCH USING CONSISTENCY ONE
UPDATE StandardString1 SET :name = :val WHERE KEY = :key1
INSERT INTO StandardString1 (KEY, :col1) VALUES (:key2, :val)
INSERT INTO StandardString1 (KEY, :col2) VALUES (:key3, :val)
DELETE :col2 FROM StandardString1 WHERE key = :key3
APPLY BATCH
""",
dict(key1="bKey1", key2="bKey2", key3="bKey3", name="bName", col1="bCol1", col2="bCol2", val="bVal"))
cursor.execute("SELECT :name FROM StandardString1 WHERE KEY = :key",
dict(name="bName", key="bKey1"))
assert cursor.rowcount == 1, "expected 1 result, got %d" % cursor.rowcount
colnames = [col_d[0] for col_d in cursor.description]
assert colnames[1] == "bName", \
"unrecognized name '%s'" % colnames[1]
r = cursor.fetchone()
assert r[1] == "bVal", \
"unrecognized value '%s'" % r[1]
cursor.execute("SELECT :name FROM StandardString1 WHERE KEY = :key",
dict(name="bCol2", key="bKey3"))
assert cursor.rowcount == 1, "expected 1 result, got %d" % cursor.rowcount
colnames = [col_d[0] for col_d in cursor.description]
assert colnames[1] == "bCol2", \
"unrecognized name '%s'" % colnames[1]
# was deleted by DELETE statement
r = cursor.fetchone()
assert r[1] == None, \
"unrecognized value '%s'" % r[1]
cursor.execute("SELECT :name FROM StandardString1 WHERE KEY = :key",
dict(name="bCol1", key="bKey2"))
assert cursor.rowcount == 1, "expected 1 result, got %d" % cursor.rowcount
colnames = [col_d[0] for col_d in cursor.description]
assert colnames[1] == "bCol1", \
"unrecognized name '%s'" % colnames[1]
r = cursor.fetchone()
assert r[1] == "bVal", \
"unrecognized value '%s'" % r[1]
assert_raises(cql.ProgrammingError,
cursor.execute,
"""
BEGIN BATCH USING CONSISTENCY ONE
UPDATE USING CONSISTENCY QUORUM StandardString1 SET 'name' = 'value' WHERE KEY = 'bKey4'
DELETE 'name' FROM StandardString1 WHERE KEY = 'bKey4'
APPLY BATCH
""")