1//  Copyright (c) 2014 Couchbase, Inc.
2//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
3//  except in compliance with the License. You may obtain a copy of the License at
4//    http://www.apache.org/licenses/LICENSE-2.0
5//  Unless required by applicable law or agreed to in writing, software distributed under the
6//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
7//  either express or implied. See the License for the specific language governing permissions
8//  and limitations under the License.
9
10package plan
11
12import (
13	"encoding/json"
14	"github.com/couchbase/query/algebra"
15	"github.com/couchbase/query/datastore"
16	"github.com/couchbase/query/errors"
17	"github.com/couchbase/query/expression"
18	"github.com/couchbase/query/expression/parser"
19	"github.com/couchbase/query/value"
20)
21
22// Create index
23type CreateIndex struct {
24	readwrite
25	keyspace datastore.Keyspace
26	node     *algebra.CreateIndex
27}
28
29func NewCreateIndex(keyspace datastore.Keyspace, node *algebra.CreateIndex) *CreateIndex {
30	return &CreateIndex{
31		keyspace: keyspace,
32		node:     node,
33	}
34}
35
36func (this *CreateIndex) Accept(visitor Visitor) (interface{}, error) {
37	return visitor.VisitCreateIndex(this)
38}
39
40func (this *CreateIndex) New() Operator {
41	return &CreateIndex{}
42}
43
44func (this *CreateIndex) Keyspace() datastore.Keyspace {
45	return this.keyspace
46}
47
48func (this *CreateIndex) Node() *algebra.CreateIndex {
49	return this.node
50}
51
52func (this *CreateIndex) MarshalJSON() ([]byte, error) {
53	return json.Marshal(this.MarshalBase(nil))
54}
55
56func (this *CreateIndex) MarshalBase(f func(map[string]interface{})) map[string]interface{} {
57	r := map[string]interface{}{"#operator": "CreateIndex"}
58	r["keyspace"] = this.keyspace.Name()
59	r["namespace"] = this.keyspace.NamespaceId()
60	r["index"] = this.node.Name()
61	k := make([]interface{}, len(this.node.Keys()))
62	for i, term := range this.node.Keys() {
63		q := make(map[string]interface{}, 2)
64		q["expr"] = term.Expression().String()
65
66		if term.Descending() {
67			q["desc"] = term.Descending()
68		}
69
70		k[i] = q
71	}
72	r["keys"] = k
73	r["using"] = this.node.Using()
74
75	if this.node.Partition() != nil && this.node.Partition().Strategy() != datastore.NO_PARTITION {
76		q := make(map[string]interface{}, 2)
77		q["exprs"] = this.node.Partition().Expressions()
78		q["strategy"] = this.node.Partition().Strategy()
79		r["partition"] = q
80	}
81
82	if this.node.Where() != nil {
83		r["where"] = this.node.Where()
84	}
85
86	if this.node.With() != nil {
87		r["with"] = this.node.With()
88	}
89
90	if f != nil {
91		f(r)
92	}
93	return r
94}
95
96func (this *CreateIndex) UnmarshalJSON(body []byte) error {
97	var _unmarshalled struct {
98		_      string `json:"#operator"`
99		Keysp  string `json:"keyspace"`
100		Namesp string `json:"namespace"`
101		Index  string `json:"index"`
102		Keys   []struct {
103			Expr string `json:"expr"`
104			Desc bool   `json:"desc"`
105		} `json:"keys"`
106		Using     datastore.IndexType `json:"using"`
107		Partition *struct {
108			Exprs    []string                `json:"exprs"`
109			Strategy datastore.PartitionType `json:"strategy"`
110		} `json:"partition"`
111		Where string          `json:"where"`
112		With  json.RawMessage `json:"with"`
113	}
114
115	err := json.Unmarshal(body, &_unmarshalled)
116	if err != nil {
117		return err
118	}
119
120	this.keyspace, err = datastore.GetKeyspace(_unmarshalled.Namesp, _unmarshalled.Keysp)
121	if err != nil {
122		return err
123	}
124
125	ksref := algebra.NewKeyspaceRef(_unmarshalled.Namesp, _unmarshalled.Keysp, "")
126
127	var expr expression.Expression
128	keys := make(algebra.IndexKeyTerms, len(_unmarshalled.Keys))
129
130	for i, term := range _unmarshalled.Keys {
131		expr, err = parser.Parse(term.Expr)
132		if err != nil {
133			return err
134		}
135		keys[i] = algebra.NewIndexKeyTerm(expr, term.Desc)
136	}
137
138	if keys.HasDescending() {
139		indexer, err1 := this.keyspace.Indexer(_unmarshalled.Using)
140		if err1 != nil {
141			return err1
142		}
143		if _, ok := indexer.(datastore.Indexer2); !ok {
144			return errors.NewIndexerDescCollationError()
145		}
146	}
147
148	var partition *algebra.IndexPartitionTerm
149	if _unmarshalled.Partition != nil {
150		exprs := make(expression.Expressions, len(_unmarshalled.Partition.Exprs))
151		for i, p := range _unmarshalled.Partition.Exprs {
152			exprs[i], err = parser.Parse(p)
153			if err != nil {
154				return err
155			}
156		}
157		partition = algebra.NewIndexPartitionTerm(_unmarshalled.Partition.Strategy, exprs)
158	}
159
160	var where expression.Expression
161	if _unmarshalled.Where != "" {
162		where, err = parser.Parse(_unmarshalled.Where)
163		if err != nil {
164			return err
165		}
166	}
167
168	var with value.Value
169	if len(_unmarshalled.With) > 0 {
170		with = value.NewValue([]byte(_unmarshalled.With))
171	}
172
173	this.node = algebra.NewCreateIndex(_unmarshalled.Index, ksref,
174		keys, partition, where, _unmarshalled.Using, with)
175	return nil
176}
177
178func (this *CreateIndex) verify(prepared *Prepared) bool {
179	var res bool
180
181	this.keyspace, res = verifyKeyspace(this.keyspace, prepared)
182	return res
183}
184