syncbase: syncQL: queries can drill into maps,sets,arrays,lists.

map/set/array/list used in the where clause
-------------------------------------------

example:
select v.Name from Customer where v.Foo["bar"] = 42

If Foo field is a map:
        The expression will evaluate to true and the record will be
        selected if the key "bar" exists in Foo and the
        corresponding value is 42.  In all other cases (no "bar" key,
        the value is not 42), the expression will evaluate to false and
        the record will not be selected.

example:
select v.Name from Customer where v.Foo["bar"] = true
If Foo is a set:
        The expression will evaluate to true and the record selected
        if Foo contains "bar".

example:
select v.Name from Customer where v.Foo[2] = 123.45

If Foo is an array or list with length >= 3:
        The expression will evaluate to true and the record will be
        selected if the value at index 2 is 123.45.  In all other cases,
        expression will evaluate to false and the record will not be
        selected.

In the examples above, if Foo is not an array, list, map or set; the expresion will
evaluate to false and no records will be selected.

map/set/array/list used in select clause
----------------------------------------

example:
select v.Foo["bar"] from Customer

If Foo is a map:
        a map lookup of "bar" will be performed.  If it exists, the resulting
        value will be returned, else a nil vdl.Value will be returned.

If Foo is a set:
        a set lookup of "bar" will be performed.  If it exists, true
        will be returned, else false will be returned.

example:
select v.Foo[2] from Customer

If Foo is an array or list with length >= 3:
       The value at index 2 will be returned, else a nil vdl.Value will be
       returned.

In both examples above, if Foo is not an array, list, map or set; nil will be returned.

Note: For simplicity, the above examples show literals as keys/indexes.  They need not be.
For example, fields and functions (or some combination thereof) will also work
For example,
select v.Foo[v.Bar] from Customer
select v.Address.Street where v.Addresses[UpperCase("primary")] == "100 Main St."

Complex function
----------------
I needed to add the Complex function so that complex literals as keys could be
specified in queries.

Change-Id: I5547827de7804c73f1abcf3e8bf6f7901bf1ecb3
diff --git a/v23/syncbase/nosql/exec_test/db_objects.vdl b/v23/syncbase/nosql/exec_test/db_objects.vdl
index fb75d36..56c819f 100644
--- a/v23/syncbase/nosql/exec_test/db_objects.vdl
+++ b/v23/syncbase/nosql/exec_test/db_objects.vdl
@@ -94,3 +94,12 @@
 	Name         string
 	TitleOrValue TitleOrValueType
 }
+
+type ArrayOfFour [4]string
+
+type KeyIndexData struct {
+	A ArrayOfFour
+	L []string
+	M map[complex128]string
+	S set[string]
+}
diff --git a/v23/syncbase/nosql/exec_test/db_objects.vdl.go b/v23/syncbase/nosql/exec_test/db_objects.vdl.go
index 321556c..ec98412 100644
--- a/v23/syncbase/nosql/exec_test/db_objects.vdl.go
+++ b/v23/syncbase/nosql/exec_test/db_objects.vdl.go
@@ -321,6 +321,25 @@
 }) {
 }
 
+type ArrayOfFour [4]string
+
+func (ArrayOfFour) __VDLReflect(struct {
+	Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/exec_test.ArrayOfFour"`
+}) {
+}
+
+type KeyIndexData struct {
+	A ArrayOfFour
+	L []string
+	M map[complex128]string
+	S map[string]struct{}
+}
+
+func (KeyIndexData) __VDLReflect(struct {
+	Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/exec_test.KeyIndexData"`
+}) {
+}
+
 func init() {
 	vdl.Register((*AddressInfo)(nil))
 	vdl.Register((*CreditAgency)(nil))
@@ -337,4 +356,6 @@
 	vdl.Register((*BarType)(nil))
 	vdl.Register((*TitleOrValueType)(nil))
 	vdl.Register((*BazType)(nil))
+	vdl.Register((*ArrayOfFour)(nil))
+	vdl.Register((*KeyIndexData)(nil))
 }
diff --git a/v23/syncbase/nosql/exec_test/exec_test.go b/v23/syncbase/nosql/exec_test/exec_test.go
index e188585..59ffde4 100644
--- a/v23/syncbase/nosql/exec_test/exec_test.go
+++ b/v23/syncbase/nosql/exec_test/exec_test.go
@@ -35,6 +35,7 @@
 var customerEntries []kv
 var numbersEntries []kv
 var fooEntries []kv
+var keyIndexDataEntries []kv
 
 var t2015 time.Time
 
@@ -56,6 +57,7 @@
 	customerTable := tu.CreateTable(t, ctx, db, "Customer")
 	numbersTable := tu.CreateTable(t, ctx, db, "Numbers")
 	fooTable := tu.CreateTable(t, ctx, db, "Foo")
+	keyIndexDataTable := tu.CreateTable(t, ctx, db, "KeyIndexData")
 
 	t20150122131101, _ := time.Parse("Jan 2 2006 15:04:05 -0700 MST", "Jan 22 2015 13:11:01 -0800 PST")
 	t20150210161202, _ := time.Parse("Jan 2 2006 15:04:05 -0700 MST", "Feb 10 2015 16:12:02 -0800 PST")
@@ -174,6 +176,18 @@
 	if err := fooTable.Put(ctx, k, f); err != nil {
 		t.Fatalf("fooTable.Put() failed: %v", err)
 	}
+
+	k = "aaa"
+	kid := KeyIndexData{
+		[4]string{"Fee", "Fi", "Fo", "Fum"},
+		[]string{"I", "smell", "the", "blood", "of", "an", "Englishman"},
+		map[complex128]string{complex(1.1, 2.2): "Be he living, or be he dead"},
+		map[string]struct{}{"I’ll grind his bones to mix my bread": {}},
+	}
+	keyIndexDataEntries = append(keyIndexDataEntries, kv{k, vdl.ValueOf(kid)})
+	if err := keyIndexDataTable.Put(ctx, k, kid); err != nil {
+		t.Fatalf("fooTable.Put() failed: %v", err)
+	}
 }
 
 type execSelectTest struct {
@@ -852,6 +866,17 @@
 				[]*vdl.Value{numbersEntries[2].value},
 			},
 		},
+		{
+			// array, list, map, set
+			"select v.A[2], v.L[6], v.M[Complex(1.1, 2.2)], v.S[\"I’ll grind his bones to mix my bread\"] from KeyIndexData",
+			[]string{"v.A[2]", "v.L[6]", "v.M[Complex]", "v.S[I’ll grind his bones to mix my bread]"},
+			[][]*vdl.Value{[]*vdl.Value{
+				vdl.ValueOf("Fo"),
+				vdl.ValueOf("Englishman"),
+				vdl.ValueOf("Be he living, or be he dead"),
+				vdl.ValueOf(true),
+			}},
+		},
 	}
 
 	for _, test := range basic {
diff --git a/v23/syncbase/nosql/internal/query/eval.go b/v23/syncbase/nosql/internal/query/eval.go
index 62e51c8..b33e975 100644
--- a/v23/syncbase/nosql/internal/query/eval.go
+++ b/v23/syncbase/nosql/internal/query/eval.go
@@ -397,7 +397,7 @@
 	if o.Type != query_parser.TypField {
 		return o
 	}
-	value, hasAltStr, altStr := ResolveField(k, v, o.Column)
+	value, hasAltStr, altStr := ResolveField(db, k, v, o.Column)
 	if value.IsNil() {
 		return nil
 	}
@@ -463,11 +463,99 @@
 	return o
 }
 
+// Resolve object with the key(s) (if a key(s) was specified).  That is, resolve the object with the
+// value of what is specified in brackets (for maps, sets. arrays and lists).
+// If no key was specified, just return the object.
+// If a key was specified, but the object is not map, set, array or list: return nil.
+// If the key resolved to nil, return nil.
+// If the key can't be converted to the required type, return nil.
+// For arrays/lists, if the index is out of bounds, return nil.
+// For maps, if key not found, return nil.
+// For sets, if key found, return true, else return false.
+func resolveWithKey(db query_db.Database, k string, v *vdl.Value, object *vdl.Value, segment query_parser.Segment) *vdl.Value {
+	for _, key := range segment.Keys {
+		o := resolveOperand(db, k, v, key)
+		if o == nil {
+			return vdl.ValueOf(nil)
+		}
+		proposedKey := valueFromResolvedOperand(o)
+		if proposedKey == nil {
+			return vdl.ValueOf(nil)
+		}
+		switch object.Kind() {
+		case vdl.Array, vdl.List:
+			// convert key to int
+			// vdl's Index function wants an int.
+			// vdl can't make an int.
+			// int is guaranteed to be at least 32-bits.
+			// So have vdl make an int32 and then convert it to an int.
+			index32 := vdl.Int32Value(0)
+			if err := vdl.Convert(index32, proposedKey); err != nil {
+				return vdl.ValueOf(nil)
+			}
+			index := int(index32.Int())
+			if index < 0 || index >= object.Len() {
+				return vdl.ValueOf(nil)
+			}
+			object = object.Index(index)
+		case vdl.Map, vdl.Set:
+			reqKeyType := object.Type().Key()
+			keyVal := vdl.ZeroValue(reqKeyType)
+			if err := vdl.Convert(keyVal, proposedKey); err != nil {
+				return vdl.ValueOf(nil)
+			}
+			if object.Kind() == vdl.Map {
+				rv := object.MapIndex(keyVal)
+				if rv != nil {
+					object = rv
+				} else {
+					return vdl.ValueOf(nil)
+				}
+			} else { // vdl.Set
+				object = vdl.ValueOf(object.ContainsKey(keyVal))
+			}
+		default:
+			return vdl.ValueOf(nil)
+		}
+	}
+	return object
+}
+
+// Return the value of a non-nil *Operand that has been resolved by resolveOperand.
+func valueFromResolvedOperand(o *query_parser.Operand) interface{} {
+	// This switch contains the possible types returned from resolveOperand.
+	switch o.Type {
+	case query_parser.TypBigInt:
+		return o.BigInt
+	case query_parser.TypBigRat:
+		return o.BigRat
+	case query_parser.TypBool:
+		return o.Bool
+	case query_parser.TypComplex:
+		return o.Complex
+	case query_parser.TypFloat:
+		return o.Float
+	case query_parser.TypInt:
+		return o.Int
+	case query_parser.TypNil:
+		return nil
+	case query_parser.TypStr:
+		return o.Str
+	case query_parser.TypTime:
+		return o.Time
+	case query_parser.TypObject:
+		return o.Object
+	case query_parser.TypUint:
+		return o.Uint
+	}
+	return nil
+}
+
 // Resolve a field.  In the special case where a type is evaluated, in addition
 // to a string being returned, and alternate string is returned.  In this case,
 // <string-value>, true, <alt-string> is returned.  In all other cases,
 // <value>,false,"" is returned.
-func ResolveField(k string, v *vdl.Value, f *query_parser.Field) (*vdl.Value, bool, string) {
+func ResolveField(db query_db.Database, k string, v *vdl.Value, f *query_parser.Field) (*vdl.Value, bool, string) {
 	if query_checker.IsKeyField(f) {
 		return vdl.StringValue(k), false, ""
 	}
@@ -483,7 +571,10 @@
 
 	object := v
 	segments := f.Segments
-	// The first segment will always be v (itself), skip it.
+	// Does v contain a key?
+	object = resolveWithKey(db, k, v, object, segments[0])
+
+	// More segments?
 	for i := 1; i < len(segments); i++ {
 		// Auto-dereference Any and Optional values
 		object = autoDereference(object)
@@ -492,6 +583,7 @@
 			if object = object.StructFieldByName(segments[i].Value); object == nil {
 				return vdl.ValueOf(nil), false, "" // field does not exist
 			}
+			object = resolveWithKey(db, k, v, object, segments[i])
 		} else if object.Kind() == vdl.Union {
 			unionType := object.Type()
 			idx, tempValue := object.UnionField()
@@ -500,6 +592,7 @@
 			} else {
 				return vdl.ValueOf(nil), false, "" // union field does not exist or is not set
 			}
+			object = resolveWithKey(db, k, v, object, segments[i])
 		} else {
 			return vdl.ValueOf(nil), false, "" // can only traverse into structs and unions
 		}
diff --git a/v23/syncbase/nosql/internal/query/query.go b/v23/syncbase/nosql/internal/query/query.go
index 736997c..1cc7783 100644
--- a/v23/syncbase/nosql/internal/query/query.go
+++ b/v23/syncbase/nosql/internal/query/query.go
@@ -5,7 +5,9 @@
 package query
 
 import (
+	"fmt"
 	"reflect"
+	"strconv"
 
 	"v.io/syncbase/v23/syncbase/nosql/internal/query/query_checker"
 	"v.io/syncbase/v23/syncbase/nosql/internal/query/query_functions"
@@ -46,7 +48,7 @@
 		switch selector.Type {
 		case query_parser.TypSelField:
 			// If field not found, nil is returned (as per specification).
-			f, _, _ := ResolveField(k, v, selector.Field)
+			f, _, _ := ResolveField(db, k, v, selector.Field)
 			projection = append(projection, f)
 		case query_parser.TypSelFunc:
 			if selector.Function.Computed {
@@ -149,6 +151,9 @@
 				sep := ""
 				for _, segment := range selector.Field.Segments {
 					columnName = columnName + sep + segment.Value
+					for _, key := range segment.Keys {
+						columnName += getSegmentKeyAsHeading(key)
+					}
 					sep = "."
 				}
 			case query_parser.TypSelFunc:
@@ -160,6 +165,52 @@
 	return columnHeaders
 }
 
+// TODO(jkline): Should we really include key/index of a map/set/array/list in the header?
+// The column names can get quite long.  Perhaps just "[]" at the end of the segment
+// would be better.  The author of the query can always use the As clause to specify a
+// better heading.  Note: for functions, just the function name is included in the header.
+// When a decision is made, it's best to be consistent for functions and key/indexes.
+func getSegmentKeyAsHeading(segKey *query_parser.Operand) string {
+	val := "["
+	switch segKey.Type {
+	case query_parser.TypBigInt:
+		val += segKey.BigInt.String()
+	case query_parser.TypBigRat:
+		val += segKey.BigRat.String()
+	case query_parser.TypComplex:
+		val += fmt.Sprintf("%g", segKey.Complex)
+	case query_parser.TypField:
+		sep := ""
+		for _, segment := range segKey.Column.Segments {
+			val += sep + segment.Value
+			for _, key := range segment.Keys {
+				val += getSegmentKeyAsHeading(key)
+			}
+			sep = "."
+		}
+	case query_parser.TypBool:
+		val += strconv.FormatBool(segKey.Bool)
+	case query_parser.TypInt:
+		val += strconv.FormatInt(segKey.Int, 10)
+	case query_parser.TypFloat:
+		val += strconv.FormatFloat(segKey.Float, 'f', -1, 64)
+	case query_parser.TypFunction:
+		val += segKey.Function.Name
+	case query_parser.TypStr:
+		val += segKey.Str
+	case query_parser.TypTime:
+		val += segKey.Time.Format("Mon Jan 2 15:04:05 -0700 MST 2006")
+	case query_parser.TypNil:
+		val += "<nil>"
+	case query_parser.TypObject:
+		val += "<object>"
+	default:
+		val += "<?>"
+	}
+	val += "]"
+	return val
+}
+
 func execSelect(db query_db.Database, s *query_parser.SelectStatement) ([]string, ResultStream, error) {
 	keyValueStream, err := s.From.Table.DBTable.Scan(*query_checker.CompileKeyRanges(s.Where))
 	if err != nil {
diff --git a/v23/syncbase/nosql/internal/query/query_functions/math_funcs.go b/v23/syncbase/nosql/internal/query/query_functions/math_funcs.go
new file mode 100644
index 0000000..cfb01ae
--- /dev/null
+++ b/v23/syncbase/nosql/internal/query/query_functions/math_funcs.go
@@ -0,0 +1,53 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package query_functions
+
+import (
+	"v.io/syncbase/v23/syncbase/nosql/internal/query/conversions"
+	"v.io/syncbase/v23/syncbase/nosql/internal/query/query_parser"
+)
+
+func complexFunc(off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
+	r, err := conversions.ConvertValueToFloat(args[0])
+	if err != nil {
+		return nil, err
+	}
+
+	i, err := conversions.ConvertValueToFloat(args[1])
+	if err != nil {
+		return nil, err
+	}
+
+	return makeComplexOp(off, complex(r.Float, i.Float)), nil
+}
+
+func twoFloatsArgsCheck(off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
+	// The two args must be convertable to floats.
+	if err := checkIfPossibleThatArgIsConvertableToFloat(args[0]); err != nil {
+		return args[0], err
+	}
+	if err := checkIfPossibleThatArgIsConvertableToFloat(args[1]); err != nil {
+		return args[1], err
+	}
+	return nil, nil
+}
+
+// If possible, check if arg is convertable to a float.  Fields and not yet computed
+// functions cannot be checked and will just return nil.
+func checkIfPossibleThatArgIsConvertableToFloat(arg *query_parser.Operand) error {
+	// If arg is a literal or an already computed function,
+	// make sure it can be converted to a float.
+	switch arg.Type {
+	case query_parser.TypBigInt, query_parser.TypBigRat, query_parser.TypBool, query_parser.TypComplex, query_parser.TypFloat, query_parser.TypInt, query_parser.TypStr, query_parser.TypTime, query_parser.TypUint:
+		_, err := conversions.ConvertValueToFloat(arg)
+		return err
+	case query_parser.TypFunction:
+		if arg.Function.Computed {
+			_, err := conversions.ConvertValueToFloat(arg.Function.RetValue)
+			return err
+		}
+	}
+	return nil
+}
diff --git a/v23/syncbase/nosql/internal/query/query_functions/query_functions.go b/v23/syncbase/nosql/internal/query/query_functions/query_functions.go
index 9fb73db..326bbf8 100644
--- a/v23/syncbase/nosql/internal/query/query_functions/query_functions.go
+++ b/v23/syncbase/nosql/internal/query/query_functions/query_functions.go
@@ -38,8 +38,11 @@
 	functions["ymdhm"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ymdhm, timeAndStringArgsCheck}
 	functions["ymdhms"] = function{[]query_parser.OperandType{query_parser.TypStr, query_parser.TypStr}, query_parser.TypTime, ymdhms, timeAndStringArgsCheck}
 	functions["now"] = function{[]query_parser.OperandType{}, query_parser.TypTime, now, nil}
+
 	functions["lowercase"] = function{[]query_parser.OperandType{query_parser.TypStr}, query_parser.TypStr, lowerCase, singleStringArgCheck}
 	functions["uppercase"] = function{[]query_parser.OperandType{query_parser.TypStr}, query_parser.TypStr, upperCase, singleStringArgCheck}
+
+	functions["complex"] = function{[]query_parser.OperandType{query_parser.TypFloat, query_parser.TypFloat}, query_parser.TypComplex, complexFunc, twoFloatsArgsCheck}
 }
 
 // Check that function exists and that the number of args passed matches the spec.
@@ -154,6 +157,14 @@
 	return &o
 }
 
+func makeComplexOp(off int64, c complex128) *query_parser.Operand {
+	var o query_parser.Operand
+	o.Off = off
+	o.Type = query_parser.TypComplex
+	o.Complex = c
+	return &o
+}
+
 func singleStringArgCheck(off int64, args []*query_parser.Operand) (*query_parser.Operand, error) {
 	return checkIfPossibleThatArgIsConvertableToString(args[0])
 }
diff --git a/v23/syncbase/nosql/internal/query/query_parser/doc.go b/v23/syncbase/nosql/internal/query/query_parser/doc.go
index b319c6f..6e09680 100644
--- a/v23/syncbase/nosql/internal/query/query_parser/doc.go
+++ b/v23/syncbase/nosql/internal/query/query_parser/doc.go
@@ -16,7 +16,11 @@
 //
 // <field> ::= <segment>[{<period><segment>}...]
 //
-// <segment> ::= <identifier>
+// <segment> ::= <identifier>[<keys>]
+//
+// <keys> ::= <key>...
+//
+// <key> ::= <left_bracket> <operand> <right_bracket>
 //
 // <from_clause> ::= FROM <table>
 //
diff --git a/v23/syncbase/nosql/internal/query/query_parser/query_parser.go b/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
index 78bd542..342860a 100644
--- a/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
+++ b/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
@@ -30,10 +30,12 @@
 	TokIDENT
 	TokINT
 	TokLEFTANGLEBRACKET
+	TokLEFTBRACKET
 	TokLEFTPAREN
 	TokMINUS
 	TokPERIOD
 	TokRIGHTANGLEBRACKET
+	TokRIGHTBRACKET
 	TokRIGHTPAREN
 	TokSTRING
 )
@@ -55,6 +57,7 @@
 
 type Segment struct {
 	Value string
+	Keys  []*Operand // Used as key(s) or index(es) to dereference map/set/array/list.
 	Node
 }
 
@@ -242,6 +245,10 @@
 		token.Tok = TokLEFTANGLEBRACKET
 	case '>':
 		token.Tok = TokRIGHTANGLEBRACKET
+	case '[':
+		token.Tok = TokLEFTBRACKET
+	case ']':
+		token.Tok = TokRIGHTBRACKET
 	case scanner.EOF:
 		token.Tok = TokEOF
 	case scanner.Ident:
@@ -374,14 +381,20 @@
 	selector.Field = &field
 	selector.Field.Off = token.Off
 	selector.Field = &field
-	var segment Segment
-	segment.Value = token.Value
-	segment.Off = token.Off
-	selector.Field.Segments = append(selector.Field.Segments, segment)
-	token = scanToken(s)
+
+	var segment *Segment
+	var err error
+	if segment, token, err = parseSegment(db, s, token); err != nil {
+		return nil, err
+	}
+	selector.Field.Segments = append(selector.Field.Segments, *segment)
 
 	// It might be a function.
 	if token.Tok == TokLEFTPAREN {
+		// Segments with a key(s) specified cannot be function calls.
+		if len(segment.Keys) != 0 {
+			return nil, syncql.NewErrUnexpected(db.GetContext(), token.Off, token.Value)
+		}
 		// switch selector to a function
 		selector.Type = TypSelFunc
 		var err error
@@ -396,11 +409,10 @@
 			if token.Tok != TokIDENT {
 				return nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
 			}
-			var segment Segment
-			segment.Value = token.Value
-			segment.Off = token.Off
-			selector.Field.Segments = append(selector.Field.Segments, segment)
-			token = scanToken(s)
+			if segment, token, err = parseSegment(db, s, token); err != nil {
+				return nil, err
+			}
+			selector.Field.Segments = append(selector.Field.Segments, *segment)
 		}
 	}
 
@@ -422,6 +434,32 @@
 	return token, nil
 }
 
+// Parse a segment. Return the segment and the next token (or error).
+// Check for a key (i.e., [<key>] following the segment).
+func parseSegment(db query_db.Database, s *scanner.Scanner, token *Token) (*Segment, *Token, error) {
+	var segment Segment
+	segment.Value = token.Value
+	segment.Off = token.Off
+	token = scanToken(s)
+
+	for token.Tok == TokLEFTBRACKET {
+		// A key to the segment is specified.
+		token = scanToken(s)
+		var key *Operand
+		var err error
+		key, token, err = parseOperand(db, s, token)
+		if err != nil {
+			return nil, nil, err
+		}
+		segment.Keys = append(segment.Keys, key)
+		if token.Tok != TokRIGHTBRACKET {
+			return nil, nil, syncql.NewErrExpected(db.GetContext(), token.Off, "]")
+		}
+		token = scanToken(s)
+	}
+	return &segment, token, nil
+}
+
 // Parse the from clause, Return FromClause and next Token or error.
 func parseFromClause(db query_db.Database, s *scanner.Scanner, token *Token) (*FromClause, *Token, error) {
 	if strings.ToLower(token.Value) != "from" {
@@ -623,11 +661,12 @@
 		operand.Type = TypField
 		var field Field
 		field.Off = token.Off
-		var segment Segment
-		segment.Off = token.Off
-		segment.Value = token.Value
-		field.Segments = append(field.Segments, segment)
-		token = scanToken(s)
+		var segment *Segment
+		var err error
+		if segment, token, err = parseSegment(db, s, token); err != nil {
+			return nil, nil, err
+		}
+		field.Segments = append(field.Segments, *segment)
 
 		// If the next token is not a period, check for true/false/nil.
 		// If true/false or nil, change this operand to a bool or nil, respectively.
@@ -638,6 +677,10 @@
 		} else if token.Tok != TokPERIOD && strings.ToLower(segment.Value) == "nil" {
 			operand.Type = TypNil
 		} else if token.Tok == TokLEFTPAREN {
+			// Segments with a key specified cannot be function calls.
+			if len(segment.Keys) != 0 {
+				return nil, nil, syncql.NewErrUnexpected(db.GetContext(), token.Off, token.Value)
+			}
 			operand.Type = TypFunction
 			var err error
 			if operand.Function, token, err = parseFunction(db, s, segment.Value, segment.Off, token); err != nil {
@@ -650,11 +693,10 @@
 				if token.Tok != TokIDENT {
 					return nil, nil, syncql.NewErrExpectedIdentifier(db.GetContext(), token.Off, token.Value)
 				}
-				var segment Segment
-				segment.Off = token.Off
-				segment.Value = token.Value
-				field.Segments = append(field.Segments, segment)
-				token = scanToken(s)
+				if segment, token, err = parseSegment(db, s, token); err != nil {
+					return nil, nil, err
+				}
+				field.Segments = append(field.Segments, *segment)
 			}
 			operand.Column = &field
 		}
@@ -964,7 +1006,11 @@
 }
 
 func (s Segment) String() string {
-	return fmt.Sprintf(" Off(%d):%s", s.Off, s.Value)
+	val := fmt.Sprintf(" Off(%d):%s", s.Off, s.Value)
+	for _, k := range s.Keys {
+		val += "[" + k.String() + "]"
+	}
+	return val
 }
 
 func (f FromClause) String() string {
diff --git a/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go b/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go
index b118e4d..a0cabd8 100644
--- a/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go
+++ b/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go
@@ -2447,6 +2447,169 @@
 			},
 			nil,
 		},
+		{
+			"select v[\"foo\"] from Customer",
+			query_parser.SelectStatement{
+				Select: &query_parser.SelectClause{
+					Selectors: []query_parser.Selector{
+						query_parser.Selector{
+							Type: query_parser.TypSelField,
+							Field: &query_parser.Field{
+								Segments: []query_parser.Segment{
+									query_parser.Segment{
+										Value: "v",
+										Keys: []*query_parser.Operand{
+											&query_parser.Operand{
+												Type: query_parser.TypStr,
+												Str:  "foo",
+												Node: query_parser.Node{Off: 9},
+											},
+										},
+										Node: query_parser.Node{Off: 7},
+									},
+								},
+								Node: query_parser.Node{Off: 7},
+							},
+							Node: query_parser.Node{Off: 7},
+						},
+					},
+				},
+				From: &query_parser.FromClause{
+					Table: query_parser.TableEntry{
+						Name: "Customer",
+						Node: query_parser.Node{Off: 21},
+					},
+					Node: query_parser.Node{Off: 16},
+				},
+				Node: query_parser.Node{Off: 0},
+			},
+			nil,
+		},
+		{
+			"select v[\"foo\"][\"bar\"] from Customer",
+			query_parser.SelectStatement{
+				Select: &query_parser.SelectClause{
+					Selectors: []query_parser.Selector{
+						query_parser.Selector{
+							Type: query_parser.TypSelField,
+							Field: &query_parser.Field{
+								Segments: []query_parser.Segment{
+									query_parser.Segment{
+										Value: "v",
+										Keys: []*query_parser.Operand{
+											&query_parser.Operand{
+												Type: query_parser.TypStr,
+												Str:  "foo",
+												Node: query_parser.Node{Off: 9},
+											},
+											&query_parser.Operand{
+												Type: query_parser.TypStr,
+												Str:  "bar",
+												Node: query_parser.Node{Off: 16},
+											},
+										},
+										Node: query_parser.Node{Off: 7},
+									},
+								},
+								Node: query_parser.Node{Off: 7},
+							},
+							Node: query_parser.Node{Off: 7},
+						},
+					},
+				},
+				From: &query_parser.FromClause{
+					Table: query_parser.TableEntry{
+						Name: "Customer",
+						Node: query_parser.Node{Off: 28},
+					},
+					Node: query_parser.Node{Off: 23},
+				},
+				Node: query_parser.Node{Off: 0},
+			},
+			nil,
+		},
+		{
+			"select v from Customer where v.Foo[v.Bar] = \"abc\"",
+			query_parser.SelectStatement{
+				Select: &query_parser.SelectClause{
+					Selectors: []query_parser.Selector{
+						query_parser.Selector{
+							Type: query_parser.TypSelField,
+							Field: &query_parser.Field{
+								Segments: []query_parser.Segment{
+									query_parser.Segment{
+										Value: "v",
+										Node:  query_parser.Node{Off: 7},
+									},
+								},
+								Node: query_parser.Node{Off: 7},
+							},
+							Node: query_parser.Node{Off: 7},
+						},
+					},
+				},
+				From: &query_parser.FromClause{
+					Table: query_parser.TableEntry{
+						Name: "Customer",
+						Node: query_parser.Node{Off: 14},
+					},
+					Node: query_parser.Node{Off: 9},
+				},
+				Where: &query_parser.WhereClause{
+					Expr: &query_parser.Expression{
+						Operand1: &query_parser.Operand{
+							Type: query_parser.TypField,
+							Column: &query_parser.Field{
+								Segments: []query_parser.Segment{
+									query_parser.Segment{
+										Value: "v",
+										Node:  query_parser.Node{Off: 29},
+									},
+									query_parser.Segment{
+										Value: "Foo",
+										Keys: []*query_parser.Operand{
+											&query_parser.Operand{
+												Type: query_parser.TypField,
+												Column: &query_parser.Field{
+													Segments: []query_parser.Segment{
+														query_parser.Segment{
+															Value: "v",
+															Node:  query_parser.Node{Off: 35},
+														},
+														query_parser.Segment{
+															Value: "Bar",
+															Node:  query_parser.Node{Off: 37},
+														},
+													},
+													Node: query_parser.Node{Off: 35},
+												},
+												Node: query_parser.Node{Off: 35},
+											},
+										},
+										Node: query_parser.Node{Off: 31},
+									},
+								},
+								Node: query_parser.Node{Off: 29},
+							},
+							Node: query_parser.Node{Off: 29},
+						},
+						Operator: &query_parser.BinaryOperator{
+							Type: query_parser.Equal,
+							Node: query_parser.Node{Off: 42},
+						},
+						Operand2: &query_parser.Operand{
+							Type: query_parser.TypStr,
+							Str:  "abc",
+							Node: query_parser.Node{Off: 44},
+						},
+						Node: query_parser.Node{Off: 29},
+					},
+					Node: query_parser.Node{Off: 23},
+				},
+				Node: query_parser.Node{Off: 0},
+			},
+			nil,
+		},
 	}
 
 	for _, test := range basic {
diff --git a/v23/syncbase/nosql/internal/query/test/db_objects.vdl b/v23/syncbase/nosql/internal/query/test/db_objects.vdl
index b520912..487f40e 100644
--- a/v23/syncbase/nosql/internal/query/test/db_objects.vdl
+++ b/v23/syncbase/nosql/internal/query/test/db_objects.vdl
@@ -24,16 +24,28 @@
         Bad
 }
 
+type RatingsArray [4]int16
+
 type EquifaxCreditReport struct {
-	Rating byte
+	Rating           byte
+	FourScoreRatings RatingsArray
+}
+
+type Tdh enum {
+	Tom
+	Dick
+	Harry
 }
 
 type ExperianCreditReport struct {
 	Rating ExperianRating
+	TdhApprovals set[Tdh]
+	Auditor Tdh
 }
 
 type TransUnionCreditReport struct {
 	Rating	int16
+	PreviousRatings map[string]int16
 }
 
 type AgencyReport union {
@@ -48,11 +60,12 @@
 }
 
 type Customer struct {
-	Name    string
-	Id      int64
-	Active  bool
-	Address AddressInfo
-	Credit  CreditReport
+	Name              string
+	Id                int64
+	Active            bool
+	Address           AddressInfo
+	PreviousAddresses []AddressInfo
+	Credit            CreditReport
 }
 
 type Invoice struct {
@@ -94,3 +107,54 @@
 	Name         string
 	TitleOrValue TitleOrValueType
 }
+
+type K struct {
+	A byte
+	B string
+}
+
+type V struct {
+	A string
+	B float32
+}
+
+type FunWithMaps struct {
+	Key K
+	Map map[K]V
+	Confusing map[int16][]set[string]
+}
+
+type ManyMaps struct {
+	B    map[bool]string
+	By   map[byte]string
+	U16  map[uint16]string
+	U32  map[uint32]string
+	U64  map[uint64]string
+	I16  map[int16]string
+	I32  map[int32]string
+	I64  map[int64]string
+	F32  map[float32]string
+	F64  map[float64]string
+	C64  map[complex64]string
+	C128 map[complex128]string
+	S    map[string]string
+	Ms   map[string]map[string]string
+	T    map[time.Time]string
+}
+
+type ManySets struct {
+	B    set[bool]
+	By   set[byte]
+	U16  set[uint16]
+	U32  set[uint32]
+	U64  set[uint64]
+	I16  set[int16]
+	I32  set[int32]
+	I64  set[int64]
+	F32  set[float32]
+	F64  set[float64]
+	C64  set[complex64]
+	C128 set[complex128]
+	S    set[string]
+	T    set[time.Time]
+}
diff --git a/v23/syncbase/nosql/internal/query/test/db_objects.vdl.go b/v23/syncbase/nosql/internal/query/test/db_objects.vdl.go
index ab62041..a402510 100644
--- a/v23/syncbase/nosql/internal/query/test/db_objects.vdl.go
+++ b/v23/syncbase/nosql/internal/query/test/db_objects.vdl.go
@@ -129,8 +129,16 @@
 }) {
 }
 
+type RatingsArray [4]int16
+
+func (RatingsArray) __VDLReflect(struct {
+	Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.RatingsArray"`
+}) {
+}
+
 type EquifaxCreditReport struct {
-	Rating byte
+	Rating           byte
+	FourScoreRatings RatingsArray
 }
 
 func (EquifaxCreditReport) __VDLReflect(struct {
@@ -138,8 +146,63 @@
 }) {
 }
 
+type Tdh int
+
+const (
+	TdhTom Tdh = iota
+	TdhDick
+	TdhHarry
+)
+
+// TdhAll holds all labels for Tdh.
+var TdhAll = [...]Tdh{TdhTom, TdhDick, TdhHarry}
+
+// TdhFromString creates a Tdh from a string label.
+func TdhFromString(label string) (x Tdh, err error) {
+	err = x.Set(label)
+	return
+}
+
+// Set assigns label to x.
+func (x *Tdh) Set(label string) error {
+	switch label {
+	case "Tom", "tom":
+		*x = TdhTom
+		return nil
+	case "Dick", "dick":
+		*x = TdhDick
+		return nil
+	case "Harry", "harry":
+		*x = TdhHarry
+		return nil
+	}
+	*x = -1
+	return fmt.Errorf("unknown label %q in test.Tdh", label)
+}
+
+// String returns the string label of x.
+func (x Tdh) String() string {
+	switch x {
+	case TdhTom:
+		return "Tom"
+	case TdhDick:
+		return "Dick"
+	case TdhHarry:
+		return "Harry"
+	}
+	return ""
+}
+
+func (Tdh) __VDLReflect(struct {
+	Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.Tdh"`
+	Enum struct{ Tom, Dick, Harry string }
+}) {
+}
+
 type ExperianCreditReport struct {
-	Rating ExperianRating
+	Rating       ExperianRating
+	TdhApprovals map[Tdh]struct{}
+	Auditor      Tdh
 }
 
 func (ExperianCreditReport) __VDLReflect(struct {
@@ -148,7 +211,8 @@
 }
 
 type TransUnionCreditReport struct {
-	Rating int16
+	Rating          int16
+	PreviousRatings map[string]int16
 }
 
 func (TransUnionCreditReport) __VDLReflect(struct {
@@ -212,11 +276,12 @@
 }
 
 type Customer struct {
-	Name    string
-	Id      int64
-	Active  bool
-	Address AddressInfo
-	Credit  CreditReport
+	Name              string
+	Id                int64
+	Active            bool
+	Address           AddressInfo
+	PreviousAddresses []AddressInfo
+	Credit            CreditReport
 }
 
 func (Customer) __VDLReflect(struct {
@@ -321,11 +386,89 @@
 }) {
 }
 
+type K struct {
+	A byte
+	B string
+}
+
+func (K) __VDLReflect(struct {
+	Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.K"`
+}) {
+}
+
+type V struct {
+	A string
+	B float32
+}
+
+func (V) __VDLReflect(struct {
+	Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.V"`
+}) {
+}
+
+type FunWithMaps struct {
+	Key       K
+	Map       map[K]V
+	Confusing map[int16][]map[string]struct{}
+}
+
+func (FunWithMaps) __VDLReflect(struct {
+	Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.FunWithMaps"`
+}) {
+}
+
+type ManyMaps struct {
+	B    map[bool]string
+	By   map[byte]string
+	U16  map[uint16]string
+	U32  map[uint32]string
+	U64  map[uint64]string
+	I16  map[int16]string
+	I32  map[int32]string
+	I64  map[int64]string
+	F32  map[float32]string
+	F64  map[float64]string
+	C64  map[complex64]string
+	C128 map[complex128]string
+	S    map[string]string
+	Ms   map[string]map[string]string
+	T    map[time.Time]string
+}
+
+func (ManyMaps) __VDLReflect(struct {
+	Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.ManyMaps"`
+}) {
+}
+
+type ManySets struct {
+	B    map[bool]struct{}
+	By   map[byte]struct{}
+	U16  map[uint16]struct{}
+	U32  map[uint32]struct{}
+	U64  map[uint64]struct{}
+	I16  map[int16]struct{}
+	I32  map[int32]struct{}
+	I64  map[int64]struct{}
+	F32  map[float32]struct{}
+	F64  map[float64]struct{}
+	C64  map[complex64]struct{}
+	C128 map[complex128]struct{}
+	S    map[string]struct{}
+	T    map[time.Time]struct{}
+}
+
+func (ManySets) __VDLReflect(struct {
+	Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/test.ManySets"`
+}) {
+}
+
 func init() {
 	vdl.Register((*AddressInfo)(nil))
 	vdl.Register((*CreditAgency)(nil))
 	vdl.Register((*ExperianRating)(nil))
+	vdl.Register((*RatingsArray)(nil))
 	vdl.Register((*EquifaxCreditReport)(nil))
+	vdl.Register((*Tdh)(nil))
 	vdl.Register((*ExperianCreditReport)(nil))
 	vdl.Register((*TransUnionCreditReport)(nil))
 	vdl.Register((*AgencyReport)(nil))
@@ -337,4 +480,9 @@
 	vdl.Register((*BarType)(nil))
 	vdl.Register((*TitleOrValueType)(nil))
 	vdl.Register((*BazType)(nil))
+	vdl.Register((*K)(nil))
+	vdl.Register((*V)(nil))
+	vdl.Register((*FunWithMaps)(nil))
+	vdl.Register((*ManyMaps)(nil))
+	vdl.Register((*ManySets)(nil))
 }
diff --git a/v23/syncbase/nosql/internal/query/test/query_test.go b/v23/syncbase/nosql/internal/query/test/query_test.go
index a3d72ab..7acf8ec 100644
--- a/v23/syncbase/nosql/internal/query/test/query_test.go
+++ b/v23/syncbase/nosql/internal/query/test/query_test.go
@@ -114,6 +114,13 @@
 var custTable table
 var numTable table
 var fooTable table
+var funWithMapsTable table
+var ratingsArrayTable table
+var tdhApprovalsTable table
+var previousRatingsTable table
+var previousAddressesTable table
+var manyMapsTable table
+var manySetsTable table
 
 type kv struct {
 	key   string
@@ -162,7 +169,7 @@
 	custTable.rows = []kv{
 		kv{
 			"001",
-			vdl.ValueOf(Customer{"John Smith", 1, true, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}, CreditReport{Agency: CreditAgencyEquifax, Report: AgencyReportEquifaxReport{EquifaxCreditReport{'A'}}}}),
+			vdl.ValueOf(Customer{"John Smith", 1, true, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}, []AddressInfo{AddressInfo{"10 Brown St.", "Mountain View", "CA", "94043"}}, CreditReport{Agency: CreditAgencyEquifax, Report: AgencyReportEquifaxReport{EquifaxCreditReport{'A', [4]int16{87, 81, 42, 2}}}}}),
 		},
 		kv{
 			"001001",
@@ -178,7 +185,7 @@
 		},
 		kv{
 			"002",
-			vdl.ValueOf(Customer{"Bat Masterson", 2, true, AddressInfo{"777 Any St.", "Collins", "IA", "50055"}, CreditReport{Agency: CreditAgencyTransUnion, Report: AgencyReportTransUnionReport{TransUnionCreditReport{80}}}}),
+			vdl.ValueOf(Customer{"Bat Masterson", 2, true, AddressInfo{"777 Any St.", "Collins", "IA", "50055"}, []AddressInfo{AddressInfo{"19 Green St.", "Boulder", "CO", "80301"}, AddressInfo{"558 W. Orange St.", "Lancaster", "PA", "17603"}}, CreditReport{Agency: CreditAgencyTransUnion, Report: AgencyReportTransUnionReport{TransUnionCreditReport{80, map[string]int16{"2015Q2": 40, "2015Q1": 60}}}}}),
 		},
 		kv{
 			"002001",
@@ -196,6 +203,10 @@
 			"002004",
 			vdl.ValueOf(Invoice{2, 1006, t20150413141707, 88, AddressInfo{"101010 Any St.", "collins", "IA", "50055"}}),
 		},
+		kv{
+			"003",
+			vdl.ValueOf(Customer{"John Steed", 3, true, AddressInfo{"100 Queen St.", "New London", "CT", "06320"}, []AddressInfo{}, CreditReport{Agency: CreditAgencyExperian, Report: AgencyReportExperianReport{ExperianCreditReport{ExperianRatingGood, map[Tdh]struct{}{TdhTom: {}, TdhHarry: {}}, TdhTom}}}}),
+		},
 	}
 	db.tables = append(db.tables, custTable)
 
@@ -228,6 +239,141 @@
 		},
 	}
 	db.tables = append(db.tables, fooTable)
+
+	funWithMapsTable.name = "FunWithMaps"
+	funWithMapsTable.rows = []kv{
+		kv{
+			"AAA",
+			vdl.ValueOf(FunWithMaps{K{'a', "bbb"}, map[K]V{K{'a', "aaa"}: V{"bbb", 23.0}, K{'a', "bbb"}: V{"ccc", 14.7}},
+				map[int16][]map[string]struct{}{
+					23: []map[string]struct{}{
+						map[string]struct{}{"foo": {}, "bar": {}},
+					},
+				},
+			}),
+		},
+		kv{
+			"BBB",
+			vdl.ValueOf(FunWithMaps{K{'x', "zzz"}, map[K]V{K{'x', "zzz"}: V{"yyy", 17.1}, K{'r', "sss"}: V{"qqq", 7.8}},
+				map[int16][]map[string]struct{}{
+					42: []map[string]struct{}{
+						map[string]struct{}{"great": {}, "dane": {}},
+						map[string]struct{}{"german": {}, "shepard": {}},
+					},
+				},
+			}),
+		},
+	}
+	db.tables = append(db.tables, funWithMapsTable)
+
+	ratingsArrayTable.name = "RatingsArray"
+	ratingsArrayTable.rows = []kv{
+		kv{
+			"000",
+			vdl.ValueOf(RatingsArray{40, 20, 10, 0}),
+		},
+		kv{
+			"111",
+			vdl.ValueOf(RatingsArray{17, 18, 19, 20}),
+		},
+	}
+	db.tables = append(db.tables, ratingsArrayTable)
+
+	tdhApprovalsTable.name = "TdhApprovals"
+	tdhApprovalsTable.rows = []kv{
+		kv{
+			"yyy",
+			vdl.ValueOf(map[Tdh]struct{}{TdhTom: {}}),
+		},
+		kv{
+			"zzz",
+			vdl.ValueOf(map[Tdh]struct{}{TdhDick: {}, TdhHarry: {}}),
+		},
+	}
+	db.tables = append(db.tables, tdhApprovalsTable)
+
+	previousRatingsTable.name = "PreviousRatings"
+	previousRatingsTable.rows = []kv{
+		kv{
+			"x1",
+			vdl.ValueOf(map[string]int16{"1Q2015": 1, "2Q2015": 2}),
+		},
+		kv{
+			"x2",
+			vdl.ValueOf(map[string]int16{"2Q2015": 3}),
+		},
+	}
+	db.tables = append(db.tables, previousRatingsTable)
+
+	previousAddressesTable.name = "PreviousAddresses"
+	previousAddressesTable.rows = []kv{
+		kv{
+			"a1",
+			vdl.ValueOf([]AddressInfo{
+				AddressInfo{"100 Main St.", "Anytown", "CA", "94303"},
+				AddressInfo{"200 Main St.", "Othertown", "IA", "51050"},
+			}),
+		},
+		kv{
+			"a2",
+			vdl.ValueOf([]AddressInfo{
+				AddressInfo{"500 Orange St", "Uptown", "ID", "83209"},
+				AddressInfo{"200 Fulton St", "Downtown", "MT", "59001"},
+			}),
+		},
+	}
+	db.tables = append(db.tables, previousAddressesTable)
+
+	manyMapsTable.name = "ManyMaps"
+	manyMapsTable.rows = []kv{
+		kv{
+			"0",
+			vdl.ValueOf(ManyMaps{
+				map[bool]string{true: "It was the best of times,"},
+				map[byte]string{10: "it was the worst of times,"},
+				map[uint16]string{16: "it was the age of wisdom,"},
+				map[uint32]string{32: "it was the age of foolishness,"},
+				map[uint64]string{64: "it was the epoch of belief,"},
+				map[int16]string{17: "it was the epoch of incredulity,"},
+				map[int32]string{33: "it was the season of Light,"},
+				map[int64]string{65: "it was the season of Darkness,"},
+				map[float32]string{32.1: "it was the spring of hope,"},
+				map[float64]string{64.2: "it was the winter of despair,"},
+				map[complex64]string{(456.789 + 10.1112i): "we had everything before us,"},
+				map[complex128]string{(123.456 + 11.2223i): "we had nothing before us,"},
+				map[string]string{"Dickens": "we are all going direct to Heaven,"},
+				map[string]map[string]string{
+					"Charles": map[string]string{"Dickens": "we are all going direct to Heaven,"},
+				},
+				map[time.Time]string{t2015_07_01_01_23_45: "we are all going direct the other way"},
+			}),
+		},
+	}
+	db.tables = append(db.tables, manyMapsTable)
+
+	manySetsTable.name = "ManySets"
+	manySetsTable.rows = []kv{
+		kv{
+			"0",
+			vdl.ValueOf(ManySets{
+				map[bool]struct{}{true: {}},
+				map[byte]struct{}{10: {}},
+				map[uint16]struct{}{16: {}},
+				map[uint32]struct{}{32: {}},
+				map[uint64]struct{}{64: {}},
+				map[int16]struct{}{17: {}},
+				map[int32]struct{}{33: {}},
+				map[int64]struct{}{65: {}},
+				map[float32]struct{}{32.1: {}},
+				map[float64]struct{}{64.2: {}},
+				map[complex64]struct{}{(456.789 + 10.1112i): {}},
+				map[complex128]struct{}{(123.456 + 11.2223i): {}},
+				map[string]struct{}{"Dickens": {}},
+				map[time.Time]struct{}{t2015_07_01_01_23_45: {}},
+			}),
+		},
+	}
+	db.tables = append(db.tables, manySetsTable)
 }
 
 type keyRangesTest struct {
@@ -295,6 +441,7 @@
 			[][]*vdl.Value{
 				[]*vdl.Value{custTable.rows[0].value},
 				[]*vdl.Value{custTable.rows[4].value},
+				[]*vdl.Value{custTable.rows[9].value},
 			},
 		},
 		{
@@ -306,6 +453,7 @@
 			[][]*vdl.Value{
 				[]*vdl.Value{custTable.rows[0].value},
 				[]*vdl.Value{custTable.rows[4].value},
+				[]*vdl.Value{custTable.rows[9].value},
 			},
 		},
 		{
@@ -325,6 +473,7 @@
 				[]*vdl.Value{custTable.rows[6].value},
 				[]*vdl.Value{custTable.rows[7].value},
 				[]*vdl.Value{custTable.rows[8].value},
+				[]*vdl.Value{custTable.rows[9].value},
 			},
 		},
 		{
@@ -364,6 +513,7 @@
 				[]*vdl.Value{custTable.rows[6].value},
 				[]*vdl.Value{custTable.rows[7].value},
 				[]*vdl.Value{custTable.rows[8].value},
+				[]*vdl.Value{custTable.rows[9].value},
 			},
 		},
 		{
@@ -374,6 +524,7 @@
 			[][]*vdl.Value{
 				[]*vdl.Value{custTable.rows[0].value},
 				[]*vdl.Value{custTable.rows[4].value},
+				[]*vdl.Value{custTable.rows[9].value},
 			},
 		},
 		{
@@ -390,6 +541,7 @@
 			[][]*vdl.Value{
 				[]*vdl.Value{vdl.ValueOf(custTable.rows[0].key), custTable.rows[0].value},
 				[]*vdl.Value{vdl.ValueOf(custTable.rows[4].key), custTable.rows[4].value},
+				[]*vdl.Value{vdl.ValueOf(custTable.rows[9].key), custTable.rows[9].value},
 			},
 		},
 		{
@@ -399,6 +551,7 @@
 			[][]*vdl.Value{
 				[]*vdl.Value{vdl.ValueOf(custTable.rows[0].key), vdl.ValueOf("John Smith")},
 				[]*vdl.Value{vdl.ValueOf(custTable.rows[4].key), vdl.ValueOf("Bat Masterson")},
+				[]*vdl.Value{vdl.ValueOf(custTable.rows[9].key), vdl.ValueOf("John Steed")},
 			},
 		},
 		{
@@ -417,6 +570,7 @@
 				[]*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int64(2))},
 				[]*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int64(2))},
 				[]*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int64(2))},
+				[]*vdl.Value{vdl.ValueOf(int64(3)), vdl.ValueOf(nil)},
 			},
 		},
 		{
@@ -529,6 +683,7 @@
 				[]*vdl.Value{vdl.ValueOf(custTable.rows[6].key), custTable.rows[6].value},
 				[]*vdl.Value{vdl.ValueOf(custTable.rows[7].key), custTable.rows[7].value},
 				[]*vdl.Value{vdl.ValueOf(custTable.rows[8].key), custTable.rows[8].value},
+				[]*vdl.Value{vdl.ValueOf(custTable.rows[9].key), custTable.rows[9].value},
 			},
 		},
 		{
@@ -587,6 +742,7 @@
 				[]*vdl.Value{vdl.ValueOf("002002")},
 				[]*vdl.Value{vdl.ValueOf("002003")},
 				[]*vdl.Value{vdl.ValueOf("002004")},
+				[]*vdl.Value{vdl.ValueOf("003")},
 			},
 		},
 		{
@@ -601,6 +757,7 @@
 				[]*vdl.Value{vdl.ValueOf("002002")},
 				[]*vdl.Value{vdl.ValueOf("002003")},
 				[]*vdl.Value{vdl.ValueOf("002004")},
+				[]*vdl.Value{vdl.ValueOf("003")},
 			},
 		},
 		{
@@ -616,6 +773,7 @@
 				[]*vdl.Value{vdl.ValueOf("002002")},
 				[]*vdl.Value{vdl.ValueOf("002003")},
 				[]*vdl.Value{vdl.ValueOf("002004")},
+				[]*vdl.Value{vdl.ValueOf("003")},
 			},
 		},
 		{
@@ -798,6 +956,7 @@
 				[]*vdl.Value{custTable.rows[6].value},
 				[]*vdl.Value{custTable.rows[7].value},
 				[]*vdl.Value{custTable.rows[8].value},
+				[]*vdl.Value{custTable.rows[9].value},
 			},
 		},
 		{
@@ -907,6 +1066,7 @@
 				[]*vdl.Value{vdl.ValueOf(t2015_07_01)},
 				[]*vdl.Value{vdl.ValueOf(t2015_07_01)},
 				[]*vdl.Value{vdl.ValueOf(t2015_07_01)},
+				[]*vdl.Value{vdl.ValueOf(t2015_07_01)},
 			},
 		},
 		// DateTime function
@@ -923,6 +1083,7 @@
 				[]*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
 				[]*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
 				[]*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
+				[]*vdl.Value{vdl.ValueOf(t2015_07_01_01_23_45)},
 			},
 		},
 		// LowerCase function
@@ -932,6 +1093,7 @@
 			[][]*vdl.Value{
 				[]*vdl.Value{vdl.ValueOf("john smith")},
 				[]*vdl.Value{vdl.ValueOf("bat masterson")},
+				[]*vdl.Value{vdl.ValueOf("john steed")},
 			},
 		},
 		// UpperCase function
@@ -941,6 +1103,7 @@
 			[][]*vdl.Value{
 				[]*vdl.Value{vdl.ValueOf("JOHN SMITH")},
 				[]*vdl.Value{vdl.ValueOf("BAT MASTERSON")},
+				[]*vdl.Value{vdl.ValueOf("JOHN STEED")},
 			},
 		},
 		// YMDHMS function
@@ -1023,6 +1186,256 @@
 			[]string{"v"},
 			[][]*vdl.Value{},
 		},
+		// Map in selection
+		{
+			"select v.Credit.Report.TransUnionReport.PreviousRatings[\"2015Q2\"] from Customer where v.Name = \"Bat Masterson\"",
+			[]string{"v.Credit.Report.TransUnionReport.PreviousRatings[2015Q2]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(int16(40))},
+			},
+		},
+		// Map in selection using function as key.
+		{
+			"select v.Credit.Report.TransUnionReport.PreviousRatings[UpperCase(\"2015q2\")] from Customer where v.Name = \"Bat Masterson\"",
+			[]string{"v.Credit.Report.TransUnionReport.PreviousRatings[UpperCase]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(int16(40))},
+			},
+		},
+		// Map in selection using struct as key.
+		{
+			"select v.Map[v.Key] from FunWithMaps",
+			[]string{"v.Map[v.Key]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(V{"ccc", 14.7})},
+				[]*vdl.Value{vdl.ValueOf(V{"yyy", 17.1})},
+			},
+		},
+		// map of int16 to array of sets of strings
+		{
+			"select v.Confusing[23][0][\"foo\"] from FunWithMaps",
+			[]string{"v.Confusing[23][0][foo]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(true)},
+				[]*vdl.Value{vdl.ValueOf(nil)},
+			},
+		},
+		// Function using a map lookup as arg
+		{
+			"select UpperCase(v.B[true]) from ManyMaps",
+			[]string{"UpperCase"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf("IT WAS THE BEST OF TIMES,")},
+			},
+		},
+		// Set in selection
+		{
+			"select v.Credit.Report.ExperianReport.TdhApprovals[\"Tom\"], v.Credit.Report.ExperianReport.TdhApprovals[\"Dick\"], v.Credit.Report.ExperianReport.TdhApprovals[\"Harry\"] from Customer where v.Name = \"John Steed\"",
+			[]string{
+				"v.Credit.Report.ExperianReport.TdhApprovals[Tom]",
+				"v.Credit.Report.ExperianReport.TdhApprovals[Dick]",
+				"v.Credit.Report.ExperianReport.TdhApprovals[Harry]",
+			},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(true), vdl.ValueOf(false), vdl.ValueOf(true)},
+			},
+		},
+		// List in selection
+		{
+			"select v.PreviousAddresses[0].Street from Customer where v.Name = \"Bat Masterson\"",
+			[]string{"v.PreviousAddresses[0].Street"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf("19 Green St.")},
+			},
+		},
+		// List in selection (index out of bounds)
+		{
+			"select v.PreviousAddresses[2].Street from Customer where v.Name = \"Bat Masterson\"",
+			[]string{"v.PreviousAddresses[2].Street"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(nil)},
+			},
+		},
+		// Array in selection
+		{
+			"select v.Credit.Report.EquifaxReport.FourScoreRatings[2] from Customer where v.Name = \"John Smith\"",
+			[]string{"v.Credit.Report.EquifaxReport.FourScoreRatings[2]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(int16(42))},
+			},
+		},
+		// Array in selection (using an array as the index)
+		// Note: v.Credit.Report.EquifaxReport.FourScoreRatings[3] is 2
+		// and v.Credit.Report.EquifaxReport.FourScoreRatings[2] is 42
+		{
+			"select v.Credit.Report.EquifaxReport.FourScoreRatings[v.Credit.Report.EquifaxReport.FourScoreRatings[3]] from Customer where v.Name = \"John Smith\"",
+			[]string{"v.Credit.Report.EquifaxReport.FourScoreRatings[v.Credit.Report.EquifaxReport.FourScoreRatings[3]]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(int16(42))},
+			},
+		},
+		// Array in selection (index out of bounds)
+		{
+			"select v.Credit.Report.EquifaxReport.FourScoreRatings[4] from Customer where v.Name = \"John Smith\"",
+			[]string{"v.Credit.Report.EquifaxReport.FourScoreRatings[4]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(nil)},
+			},
+		},
+		// Map in where expression
+		{
+			"select v.Name from Customer where v.Credit.Report.TransUnionReport.PreviousRatings[\"2015Q2\"] = 40",
+			[]string{"v.Name"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf("Bat Masterson")},
+			},
+		},
+		// Set in where expression (convert string to enum to do lookup)
+		{
+			"select v.Name from Customer where v.Credit.Report.ExperianReport.TdhApprovals[\"Tom\"] = true",
+			[]string{
+				"v.Name",
+			},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf("John Steed")},
+			},
+		},
+		// Negative case: Set in where expression (convert string to enum to do lookup)
+		{
+			"select v.Name from Customer where v.Credit.Report.ExperianReport.TdhApprovals[\"Dick\"] = true",
+			[]string{
+				"v.Name",
+			},
+			[][]*vdl.Value{},
+		},
+		// Set in where expression (use another field as lookup key)
+		// Find all customers where experian auditor was also an approver.
+		{
+			"select v.Name from Customer where v.Credit.Report.ExperianReport.TdhApprovals[v.Credit.Report.ExperianReport.Auditor] = true",
+			[]string{
+				"v.Name",
+			},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf("John Steed")},
+			},
+		},
+		// List in where expression
+		{
+			"select v.Name from Customer where v.PreviousAddresses[0].Street = \"19 Green St.\"",
+			[]string{"v.Name"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf("Bat Masterson")},
+			},
+		},
+		// List in where expression (index out of bounds)
+		{
+			"select v.Name from Customer where v.PreviousAddresses[10].Street = \"19 Green St.\"",
+			[]string{"v.Name"},
+			[][]*vdl.Value{},
+		},
+		// Array in where expression
+		{
+			"select v.Name from Customer where v.Credit.Report.EquifaxReport.FourScoreRatings[2] = 42",
+			[]string{"v.Name"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf("John Smith")},
+			},
+		},
+		// Array in where expression (using another field as index)
+		// Note: v.Credit.Report.EquifaxReport.FourScoreRatings[3] is 2
+		// and v.Credit.Report.EquifaxReport.FourScoreRatings[2] is 42
+		{
+			"select v.Name from Customer where v.Credit.Report.EquifaxReport.FourScoreRatings[v.Credit.Report.EquifaxReport.FourScoreRatings[3]] = 42",
+			[]string{"v.Name"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf("John Smith")},
+			},
+		},
+		// Array in where expression (index out of bounds, using another field as index)
+		{
+			"select v.Name from Customer where v.Credit.Report.EquifaxReport.FourScoreRatings[v.Credit.Report.EquifaxReport.FourScoreRatings[2]] = 42",
+			[]string{"v.Name"},
+			[][]*vdl.Value{},
+		},
+		// Array in select and where expressions (top level value is the array)
+		{
+			"select v[0], v[1], v[2], v[3] from RatingsArray where v[0] = 40",
+			[]string{"v[0]", "v[1]", "v[2]", "v[3]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(int16(40)), vdl.ValueOf(int16(20)), vdl.ValueOf(int16(10)), vdl.ValueOf(int16(0))},
+			},
+		},
+		// List in select and where expressions (top level value is the list)
+		{
+			"select v[-1].City, v[0].City, v[1].City, v[2].City from PreviousAddresses where v[1].Street = \"200 Main St.\"",
+			[]string{"v[-1].City", "v[0].City", "v[1].City", "v[2].City"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf("Anytown"), vdl.ValueOf("Othertown"), vdl.ValueOf(nil)},
+			},
+		},
+		// Set in select and where expressions (top level value is the set)
+		{
+			"select v[\"Tom\"], v[\"Dick\"], v[\"Harry\"] from TdhApprovals where v[\"Dick\"] = true",
+			[]string{"v[Tom]", "v[Dick]", "v[Harry]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(false), vdl.ValueOf(true), vdl.ValueOf(true)},
+			},
+		},
+		// Map in select and where expressions (top level value is the map)
+		{
+			"select v[\"1Q2015\"], v[\"2Q2015\"] from PreviousRatings where v[\"2Q2015\"] = 3",
+			[]string{"v[1Q2015]", "v[2Q2015]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{vdl.ValueOf(nil), vdl.ValueOf(int16(3))},
+			},
+		},
+		// Test lots of types as map keys
+		{
+			"select v.B[true], v.By[10], v.U16[16], v.U32[32], v.U64[64], v.I16[17], v.I32[33], v.I64[65], v.F32[32.1], v.F64[64.2], v.C64[Complex(456.789, 10.1112)], v.C128[Complex(123.456, 11.2223)], v.S[\"Dickens\"], v.Ms[\"Charles\"][\"Dickens\"], v.T[DateTime(\"2015-07-01 01:23:45 PDT\")] from ManyMaps",
+			[]string{"v.B[true]", "v.By[10]", "v.U16[16]", "v.U32[32]", "v.U64[64]", "v.I16[17]", "v.I32[33]", "v.I64[65]", "v.F32[32.1]", "v.F64[64.2]", "v.C64[Complex]", "v.C128[Complex]", "v.S[Dickens]", "v.Ms[Charles][Dickens]", "v.T[DateTime]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{
+					vdl.ValueOf("It was the best of times,"),
+					vdl.ValueOf("it was the worst of times,"),
+					vdl.ValueOf("it was the age of wisdom,"),
+					vdl.ValueOf("it was the age of foolishness,"),
+					vdl.ValueOf("it was the epoch of belief,"),
+					vdl.ValueOf("it was the epoch of incredulity,"),
+					vdl.ValueOf("it was the season of Light,"),
+					vdl.ValueOf("it was the season of Darkness,"),
+					vdl.ValueOf("it was the spring of hope,"),
+					vdl.ValueOf("it was the winter of despair,"),
+					vdl.ValueOf("we had everything before us,"),
+					vdl.ValueOf("we had nothing before us,"),
+					vdl.ValueOf("we are all going direct to Heaven,"),
+					vdl.ValueOf("we are all going direct to Heaven,"),
+					vdl.ValueOf("we are all going direct the other way"),
+				},
+			},
+		},
+		// Test lots of types as set keys
+		{
+			"select v.B[true], v.By[10], v.U16[16], v.U32[32], v.U64[64], v.I16[17], v.I32[33], v.I64[65], v.F32[32.1], v.F64[64.2], v.C64[Complex(456.789, 10.1112)], v.C128[Complex(123.456, 11.2223)], v.S[\"Dickens\"], v.T[DateTime(\"2015-07-01 01:23:45 PDT\")] from ManySets",
+			[]string{"v.B[true]", "v.By[10]", "v.U16[16]", "v.U32[32]", "v.U64[64]", "v.I16[17]", "v.I32[33]", "v.I64[65]", "v.F32[32.1]", "v.F64[64.2]", "v.C64[Complex]", "v.C128[Complex]", "v.S[Dickens]", "v.T[DateTime]"},
+			[][]*vdl.Value{
+				[]*vdl.Value{
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+					vdl.ValueOf(true),
+				},
+			},
+		},
 	}
 
 	for _, test := range basic {
@@ -1488,10 +1901,18 @@
 			numTable.rows[0].key, numTable.rows[0].value, true,
 		},
 		{
+			"select k, v from Numbers where v.C64 = Complex(123, 7)",
+			numTable.rows[0].key, numTable.rows[0].value, true,
+		},
+		{
 			"select k, v from Numbers where v.C128 = \"(456.789+10.1112i)\"",
 			numTable.rows[0].key, numTable.rows[0].value, true,
 		},
 		{
+			"select k, v from Numbers where v.C128 = Complex(456.789, 10.1112)",
+			numTable.rows[0].key, numTable.rows[0].value, true,
+		},
+		{
 			"select k, v from Numbers where v.Ui16 = 1234",
 			numTable.rows[0].key, numTable.rows[0].value, true,
 		},
@@ -1826,7 +2247,7 @@
 	}
 
 	for _, test := range basic {
-		r, _, _ := query.ResolveField(test.k, test.v, &test.f)
+		r, _, _ := query.ResolveField(db, test.k, test.v, &test.f)
 		if !reflect.DeepEqual(r, test.r) {
 			t.Errorf("got %v(%s), want %v(%s)", r, r.Type(), test.r, test.r.Type())
 		}