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 algebra
11
12import (
13	"encoding/json"
14
15	"github.com/couchbase/query/auth"
16	"github.com/couchbase/query/errors"
17	"github.com/couchbase/query/expression"
18)
19
20const (
21	KS_ANSI_JOIN    = 1 << iota // right-hand side of ANSI JOIN
22	KS_ANSI_NEST                // right-hand side of ANSI NEST
23	KS_PRIMARY_JOIN             // join on primary key (meta().id)
24	KS_UNDER_NL                 // inner side of nested-loop join
25	KS_UNDER_HASH               // right-hand side of Hash Join
26)
27
28/*
29Represents the Keyspace (bucket) term in the FROM clause.  The
30keyspace can be prefixed with an optional namespace (pool).
31
32Nested paths can be specified. For each document in the keyspace the
33path is evaluated and its value becomes an input to the query. If any
34element of the path is NULL or missing, the document is skipped and
35does not contribute to the query.
36
37The alias for the FROM clause is specified using the AS keyword.
38
39Specific primary keys within a keyspace can be specified.  Only values
40having those primary keys will be included as inputs to the query.
41*/
42type KeyspaceTerm struct {
43	namespace string
44	keyspace  string
45	as        string
46	keys      expression.Expression
47	indexes   IndexRefs
48	joinHint  JoinHint
49	property  uint32
50}
51
52func NewKeyspaceTerm(namespace, keyspace string, as string,
53	keys expression.Expression, indexes IndexRefs) *KeyspaceTerm {
54	return &KeyspaceTerm{namespace, keyspace, as, keys, indexes, JOIN_HINT_NONE, 0}
55}
56
57func (this *KeyspaceTerm) Accept(visitor NodeVisitor) (interface{}, error) {
58	return visitor.VisitKeyspaceTerm(this)
59}
60
61/*
62This method maps all the constituent terms, namely keys in the FROM
63clause.
64*/
65func (this *KeyspaceTerm) MapExpressions(mapper expression.Mapper) (err error) {
66	if this.keys != nil {
67		this.keys, err = mapper.Map(this.keys)
68		if err != nil {
69			return err
70		}
71	}
72
73	return
74}
75
76/*
77   Returns all contained Expressions.
78*/
79func (this *KeyspaceTerm) Expressions() expression.Expressions {
80	exprs := make(expression.Expressions, 0, 1)
81	if this.keys != nil {
82		exprs = append(exprs, this.keys)
83	}
84
85	return exprs
86}
87
88/*
89Returns all required privileges.
90*/
91func (this *KeyspaceTerm) Privileges() (*auth.Privileges, errors.Error) {
92	privs, err := privilegesFromKeyspace(this.namespace, this.keyspace)
93	if err != nil {
94		return privs, err
95	}
96	if this.keys != nil {
97		privs.AddAll(this.keys.Privileges())
98	}
99	return privs, nil
100}
101
102func privilegesFromKeyspace(namespace, keyspace string) (*auth.Privileges, errors.Error) {
103	privs := auth.NewPrivileges()
104	fullKeyspace := namespace + ":" + keyspace
105	if namespace == "#system" {
106		switch keyspace {
107		case "user_info", "applicable_roles":
108			privs.Add(fullKeyspace, auth.PRIV_SECURITY_READ)
109		case "keyspaces", "indexes", "my_user_info":
110			// Do nothing. These tables handle security internally, by
111			// filtering the results.
112		case "datastores", "namespaces", "dual":
113			// Do nothing. These three tables are open to all.
114		default:
115			privs.Add(fullKeyspace, auth.PRIV_SYSTEM_READ)
116		}
117	} else {
118		privs.Add(fullKeyspace, auth.PRIV_QUERY_SELECT)
119	}
120	return privs, nil
121}
122
123/*
124   Representation as a N1QL string.
125*/
126func (this *KeyspaceTerm) String() string {
127	return this.toString(false)
128}
129
130/*
131   Representation as a N1QL string.
132*/
133func (this *KeyspaceTerm) toString(join bool) string {
134	s := ""
135
136	if this.namespace != "" {
137		s += "`" + this.namespace + "`:"
138	}
139
140	s += "`" + this.keyspace + "`"
141
142	if this.as != "" {
143		s += " as `" + this.as + "`"
144	}
145
146	if this.keys != nil {
147		if join {
148			s += " on keys " + this.keys.String()
149		} else {
150			s += " use keys " + this.keys.String()
151		}
152	}
153
154	// since use keys cannot be mixed with join hints, we can safely add the "use" keyword
155	switch this.joinHint {
156	case USE_HASH_BUILD:
157		s += " use hash(build)"
158	case USE_HASH_PROBE:
159		s += " use hash(probe)"
160	case USE_NL:
161		s += " use nl"
162	}
163
164	return s
165}
166
167/*
168Qualify all identifiers for the parent expression. Checks for
169duplicate aliases.
170*/
171func (this *KeyspaceTerm) Formalize(parent *expression.Formalizer) (f *expression.Formalizer, err error) {
172	var errString string
173	keyspace := this.Alias()
174	if keyspace == "" {
175		if this.IsAnsiJoin() {
176			errString = "JOIN"
177		} else if this.IsAnsiNest() {
178			errString = "NEST"
179		} else {
180			errString = "FROM"
181		}
182		err = errors.NewNoTermNameError(errString, "plan.keyspace.requires_name_or_alias")
183		return
184	}
185
186	if this.IsAnsiJoinOp() {
187		f = parent
188	} else {
189		f = expression.NewFormalizer("", parent)
190	}
191
192	if this.keys != nil {
193		_, err = this.keys.Accept(f)
194		if err != nil {
195			return
196		}
197	}
198
199	_, ok := parent.Allowed().Field(keyspace)
200	if ok {
201		if this.IsAnsiJoin() {
202			errString = "JOIN"
203		} else if this.IsAnsiNest() {
204			errString = "NEST"
205		} else {
206			errString = "subquery"
207		}
208		err = errors.NewDuplicateAliasError(errString, keyspace, "plan.keyspace.duplicate_alias")
209		return nil, err
210	}
211
212	if this.IsAnsiJoinOp() {
213		f.SetKeyspace("")
214		f.SetAllowedAlias(keyspace, true)
215		f.SetAlias(this.As())
216	} else {
217		f.SetAlias(this.As())
218		f.SetKeyspace(keyspace)
219	}
220	return
221}
222
223/*
224Return the primary term in the FROM clause.
225*/
226func (this *KeyspaceTerm) PrimaryTerm() FromTerm {
227	return this
228}
229
230/*
231Returns the alias string.
232*/
233func (this *KeyspaceTerm) Alias() string {
234	if this.as != "" {
235		return this.as
236	} else {
237		return this.keyspace
238	}
239}
240
241/*
242Returns the namespace string.
243*/
244func (this *KeyspaceTerm) Namespace() string {
245	return this.namespace
246}
247
248/*
249Set the namespace string when it is empty.
250*/
251func (this *KeyspaceTerm) SetDefaultNamespace(namespace string) {
252	if this.namespace == "" {
253		this.namespace = namespace
254	}
255}
256
257/*
258Returns the keyspace string (buckets).
259*/
260func (this *KeyspaceTerm) Keyspace() string {
261	return this.keyspace
262}
263
264/*
265Returns the explicit alias.
266*/
267func (this *KeyspaceTerm) As() string {
268	return this.as
269}
270
271/*
272Returns the keys expression defined by the USE KEYS
273clause.
274*/
275func (this *KeyspaceTerm) Keys() expression.Expression {
276	return this.keys
277}
278
279/*
280Returns the indexes defined by the USE INDEX clause.
281*/
282func (this *KeyspaceTerm) Indexes() IndexRefs {
283	return this.indexes
284}
285
286/*
287Returns the join hint (USE HASH or USE NL).
288*/
289func (this *KeyspaceTerm) JoinHint() JoinHint {
290	return this.joinHint
291}
292
293/*
294Join hint prefers hash join
295*/
296func (this *KeyspaceTerm) PreferHash() bool {
297	return this.joinHint == USE_HASH_BUILD || this.joinHint == USE_HASH_PROBE
298}
299
300/*
301Join hint prefers nested loop join
302*/
303func (this *KeyspaceTerm) PreferNL() bool {
304	return this.joinHint == USE_NL
305}
306
307/*
308Returns the property.
309*/
310func (this *KeyspaceTerm) Property() uint32 {
311	return this.property
312}
313
314/*
315Returns whether this keyspace is for an ANSI JOIN
316*/
317func (this *KeyspaceTerm) IsAnsiJoin() bool {
318	return (this.property & KS_ANSI_JOIN) != 0
319}
320
321/*
322Returns whether this keyspace is for an ANSI NEST
323*/
324func (this *KeyspaceTerm) IsAnsiNest() bool {
325	return (this.property & KS_ANSI_NEST) != 0
326}
327
328/*
329Returns whether this keyspace is for an ANSI JOIN or ANSI NEST
330*/
331func (this *KeyspaceTerm) IsAnsiJoinOp() bool {
332	return (this.property & (KS_ANSI_JOIN | KS_ANSI_NEST)) != 0
333}
334
335/*
336Returns whether joining on primary key (meta().id)
337*/
338func (this *KeyspaceTerm) IsPrimaryJoin() bool {
339	if this.IsAnsiJoinOp() {
340		return (this.property & KS_PRIMARY_JOIN) != 0
341	} else {
342		return false
343	}
344}
345
346/*
347Returns whether under inner of nested-loop join
348*/
349func (this *KeyspaceTerm) IsUnderNL() bool {
350	return (this.property & KS_UNDER_NL) != 0
351}
352
353/*
354Returns whether this keyspace is being considered for Hash Join
355*/
356func (this *KeyspaceTerm) IsUnderHash() bool {
357	return (this.property & KS_UNDER_HASH) != 0
358}
359
360/*
361Set join keys
362*/
363func (this *KeyspaceTerm) SetJoinKeys(keys expression.Expression) {
364	this.keys = keys
365}
366
367/*
368Set join hint
369*/
370func (this *KeyspaceTerm) SetJoinHint(joinHint JoinHint) {
371	this.joinHint = joinHint
372}
373
374/*
375Set property
376*/
377func (this *KeyspaceTerm) SetProperty(property uint32) {
378	this.property = property
379}
380
381/*
382Set ANSI JOIN property
383*/
384func (this *KeyspaceTerm) SetAnsiJoin() {
385	this.property |= KS_ANSI_JOIN
386}
387
388/*
389Set ANSI NEST property
390*/
391func (this *KeyspaceTerm) SetAnsiNest() {
392	this.property |= KS_ANSI_NEST
393}
394
395/*
396Set PRIMARY JOIN property
397*/
398func (this *KeyspaceTerm) SetPrimaryJoin() {
399	if this.IsAnsiJoinOp() {
400		this.property |= KS_PRIMARY_JOIN
401	}
402}
403
404/*
405Set UNDER NL property
406*/
407func (this *KeyspaceTerm) SetUnderNL() {
408	this.property |= KS_UNDER_NL
409}
410
411/*
412Unset UNDER NL property
413*/
414func (this *KeyspaceTerm) UnsetUnderNL() {
415	this.property &^= KS_UNDER_NL
416}
417
418/*
419Set UNDER HASH property
420*/
421func (this *KeyspaceTerm) SetUnderHash() {
422	this.property |= KS_UNDER_HASH
423}
424
425/*
426Unset UNDER HASH property
427*/
428func (this *KeyspaceTerm) UnsetUnderHash() {
429	this.property &^= KS_UNDER_HASH
430}
431
432/*
433Marshals the input keyspace into a byte array.
434*/
435func (this *KeyspaceTerm) MarshalJSON() ([]byte, error) {
436	r := map[string]interface{}{"type": "keyspaceTerm"}
437	r["as"] = this.as
438	if this.keys != nil {
439		r["keys"] = expression.NewStringer().Visit(this.keys)
440	}
441	r["namespace"] = this.namespace
442	r["keyspace"] = this.keyspace
443	return json.Marshal(r)
444}
445