Browse Source

Merge branch 'master' of https://github.com/gocql/gocql

Conflicts:
	AUTHORS
xoraes 11 years ago
parent
commit
096ad18ea1
5 changed files with 84 additions and 21 deletions
  1. 2 1
      AUTHORS
  2. 42 0
      cassandra_test.go
  3. 2 3
      conn.go
  4. 4 7
      integration.sh
  5. 34 10
      session.go

+ 2 - 1
AUTHORS

@@ -31,4 +31,5 @@ Muir Manders <muir@retailnext.net>
 Sankar P <sankar.curiosity@gmail.com>
 Julien Da Silva <julien.dasilva@gmail.com>
 Dan Kennedy <daniel@firstcs.co.uk>
-Nick Dhupia<nick.dhupia@gmail.com>
+Nick Dhupia<nick.dhupia@gmail.com>
+Yasuharu Goto <matope.ono@gmail.com>

+ 42 - 0
cassandra_test.go

@@ -299,6 +299,48 @@ func TestCAS(t *testing.T) {
 	}
 }
 
+func TestMapScanCAS(t *testing.T) {
+	if *flagProto == 1 {
+		t.Skip("lightweight transactions not supported. Please use Cassandra >= 2.0")
+	}
+
+	session := createSession(t)
+	defer session.Close()
+
+	if err := createTable(session, `CREATE TABLE cas_table2 (
+			title         varchar,
+			revid   	  timeuuid,
+			last_modified timestamp,
+			deleted boolean,
+			PRIMARY KEY (title, revid)
+		)`); err != nil {
+		t.Fatal("create:", err)
+	}
+
+	title, revid, modified, deleted := "baz", TimeUUID(), time.Now(), false
+	mapCAS := map[string]interface{}{}
+
+	if applied, err := session.Query(`INSERT INTO cas_table2 (title, revid, last_modified, deleted)
+		VALUES (?, ?, ?, ?) IF NOT EXISTS`,
+		title, revid, modified, deleted).MapScanCAS(mapCAS); err != nil {
+		t.Fatal("insert:", err)
+	} else if !applied {
+		t.Fatal("insert should have been applied")
+	}
+
+	mapCAS = map[string]interface{}{}
+	if applied, err := session.Query(`INSERT INTO cas_table2 (title, revid, last_modified, deleted)
+		VALUES (?, ?, ?, ?) IF NOT EXISTS`,
+		title, revid, modified, deleted).MapScanCAS(mapCAS); err != nil {
+		t.Fatal("insert:", err)
+	} else if applied {
+		t.Fatal("insert should not have been applied")
+	} else if title != mapCAS["title"] || revid != mapCAS["revid"] || deleted != mapCAS["deleted"] {
+		t.Fatalf("expected %s/%v/%v/%v but got %s/%v/%v%v", title, revid, modified, false, mapCAS["title"], mapCAS["revid"], mapCAS["last_modified"], mapCAS["deleted"])
+	}
+
+}
+
 func TestBatch(t *testing.T) {
 	if *flagProto == 1 {
 		t.Skip("atomic batches not supported. Please use Cassandra >= 2.0")

+ 2 - 3
conn.go

@@ -99,13 +99,12 @@ func Connect(addr string, cfg ConnConfig, pool ConnectionPool) (*Conn, error) {
 		pem, err := ioutil.ReadFile(cfg.SslOpts.CaPath)
 		certPool := x509.NewCertPool()
 		if !certPool.AppendCertsFromPEM(pem) {
-			panic("Failed parsing or appending certs")
+			return nil, errors.New("Failed parsing or appending certs")
 		}
 		mycert, err := tls.LoadX509KeyPair(cfg.SslOpts.CertPath, cfg.SslOpts.KeyPath)
 		if err != nil {
-			panic(err)
+			return nil, err
 		}
-
 		config := tls.Config{
 			Certificates: []tls.Certificate{mycert},
 			RootCAs:      certPool,

+ 4 - 7
integration.sh

@@ -24,18 +24,15 @@ function run_tests() {
 
 	cat results
 	cover=`cat results | grep coverage: | grep -o "[0-9]\{1,3\}" | head -n 1`
-	if [[ $cover -lt "60" ]]; then
-		echo "--- FAIL: expected coverage of at least 64 %, but coverage was $cover %"
+	if [[ $cover -lt "63" ]]; then
+		echo "--- FAIL: expected coverage of at least 63 %, but coverage was $cover %"
 		exit 1
 	fi
 	ccm clear
 
-	#cannot do this due to https://github.com/pcmanus/ccm/issues/171
-	#ccm updateconf -y testdata/cassandra.yaml
-	cp -f testdata/cassandra.yaml ~/.ccm/repository/$version/conf/
-	ccm updateconf
+	ccm updateconf 'client_encryption_options.enabled: true' 'client_encryption_options.keystore: testdata/pki/.keystore' 'client_encryption_options.keystore_password: cassandra' 'client_encryption_options.require_client_auth: true' 'client_encryption_options.truststore: testdata/pki/.truststore' 'client_encryption_options.truststore_password: cassandra'
     ccm start
     ccm status
-    go test -v -run Wiki -runssl -proto=$proto -rf=3 -cluster=$(ccm liveset) -clusterSize=$clusterSize -autowait=2000ms ./...
+    go test -cover -v -run Wiki -runssl -proto=$proto -rf=3 -cluster=$(ccm liveset) -clusterSize=$clusterSize -autowait=2000ms ./...
 }
 run_tests $1

+ 34 - 10
session.go

@@ -322,11 +322,8 @@ func (q *Query) Iter() *Iter {
 // were selected, ErrNotFound is returned.
 func (q *Query) Scan(dest ...interface{}) error {
 	iter := q.Iter()
-	if iter.err != nil {
-		return iter.err
-	}
-	if len(iter.rows) == 0 {
-		return ErrNotFound
+	if err := iter.checkErrAndNotFound(); err != nil {
+		return err
 	}
 	iter.Scan(dest...)
 	return iter.Close()
@@ -338,11 +335,8 @@ func (q *Query) Scan(dest ...interface{}) error {
 // in dest.
 func (q *Query) ScanCAS(dest ...interface{}) (applied bool, err error) {
 	iter := q.Iter()
-	if iter.err != nil {
-		return false, iter.err
-	}
-	if len(iter.rows) == 0 {
-		return false, ErrNotFound
+	if err := iter.checkErrAndNotFound(); err != nil {
+		return false, err
 	}
 	if len(iter.Columns()) > 1 {
 		dest = append([]interface{}{&applied}, dest...)
@@ -353,6 +347,26 @@ func (q *Query) ScanCAS(dest ...interface{}) (applied bool, err error) {
 	return applied, iter.Close()
 }
 
+// MapScanCAS executes a lightweight transaction (i.e. an UPDATE or INSERT
+// statement containing an IF clause). If the transaction fails because
+// the existing values did not match, the previos values will be stored
+// in dest map.
+//
+// As for INSERT .. IF NOT EXISTS, previous values will be returned as if
+// SELECT * FROM. So using ScanCAS with INSERT is inherently prone to
+// column mismatching. MapScanCAS is added to capture them safely.
+func (q *Query) MapScanCAS(dest map[string]interface{}) (applied bool, err error) {
+	iter := q.Iter()
+	if err := iter.checkErrAndNotFound(); err != nil {
+		return false, err
+	}
+	iter.MapScan(dest)
+	applied = dest["[applied]"].(bool)
+	delete(dest, "[applied]")
+
+	return applied, iter.Close()
+}
+
 // Iter represents an iterator that can be used to iterate over all rows that
 // were returned by a query. The iterator might send additional queries to the
 // database during the iteration if paging was enabled.
@@ -415,6 +429,16 @@ func (iter *Iter) Close() error {
 	return iter.err
 }
 
+// checkErrAndNotFound handle error and NotFound in one method.
+func (iter *Iter) checkErrAndNotFound() error {
+	if iter.err != nil {
+		return iter.err
+	} else if len(iter.rows) == 0 {
+		return ErrNotFound
+	}
+	return nil
+}
+
 type nextIter struct {
 	qry  Query
 	pos  int