1// Copyright (c) 2017 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 system 11 12import ( 13 "fmt" 14 15 "github.com/couchbase/query/datastore" 16 "github.com/couchbase/query/distributed" 17 "github.com/couchbase/query/errors" 18 "github.com/couchbase/query/value" 19) 20 21// Keyspace stuff 22 23type keyspaceBase struct { 24 namespace *namespace 25} 26 27func (this *keyspaceBase) Namespace() datastore.Namespace { 28 return this.namespace 29} 30 31func setKeyspaceBase(base *keyspaceBase, namespace *namespace) { 32 base.namespace = namespace 33} 34 35// Index stuff 36 37type indexBase struct { 38 indexer datastore.Indexer 39} 40 41func (this *indexBase) Indexer() datastore.Indexer { 42 return this.indexer 43} 44 45func setIndexBase(base *indexBase, indexer datastore.Indexer) { 46 base.indexer = indexer 47} 48 49type compiledSpan struct { 50 low string 51 high string 52 evalLow func(val, key string) bool 53 evalHigh func(val, key string) bool 54 55 // golang does not allow to compare functional pointers to functions... 56 equality bool 57} 58 59func compileSpan(span *datastore.Span) (*compiledSpan, errors.Error) { 60 var err errors.Error 61 var isLowValued, isHighValued bool 62 63 // currently system indexes are either primary or on a single field 64 if len(span.Seek) > 1 || len(span.Range.Low) > 1 || len(span.Range.High) > 1 { 65 return nil, errors.NewSystemDatastoreError(nil, "Invalid number of fields in span") 66 } 67 68 spanEvaluator := &compiledSpan{} 69 if span.Seek != nil { 70 val := span.Seek[0].Actual() 71 switch t := val.(type) { 72 case string: 73 default: 74 return nil, errors.NewSystemDatastoreError(nil, fmt.Sprintf("Invalid seek value %v of type %T.", t, val)) 75 } 76 spanEvaluator.low = val.(string) 77 spanEvaluator.high = spanEvaluator.low 78 spanEvaluator.evalLow = equals 79 spanEvaluator.evalHigh = noop 80 spanEvaluator.equality = true 81 } else { 82 spanEvaluator.low, spanEvaluator.evalLow, isLowValued, err = compileRange(span.Range.Low, span.Range.Inclusion, datastore.LOW) 83 if err != nil { 84 return nil, err 85 } 86 spanEvaluator.high, spanEvaluator.evalHigh, isHighValued, err = compileRange(span.Range.High, span.Range.Inclusion, datastore.HIGH) 87 if err != nil { 88 return nil, err 89 } 90 if spanEvaluator.high == spanEvaluator.low && isLowValued && isHighValued { 91 spanEvaluator.evalLow = equals 92 spanEvaluator.evalHigh = noop 93 spanEvaluator.equality = true 94 } 95 } 96 return spanEvaluator, nil 97} 98 99func (this *compiledSpan) evaluate(key string) bool { 100 return this.evalHigh(this.high, key) && this.evalLow(this.low, key) 101} 102 103func (this *compiledSpan) isEquals() bool { 104 return this.equality 105} 106 107func (this *compiledSpan) key() string { 108 return this.low 109} 110 111func compileRange(in value.Values, incl, side datastore.Inclusion) (string, func(string, string) bool, bool, errors.Error) { 112 if len(in) == 0 { 113 return "", noop, false, nil 114 } 115 val := in[0].Actual() 116 t := in[0].Type() 117 switch t { 118 case value.STRING: 119 case value.NULL: 120 121 // > null is a noop, < null should never occur and it's an error 122 if side == datastore.LOW { 123 return "", noop, false, nil 124 } 125 fallthrough 126 default: 127 return "", nil, false, errors.NewSystemDatastoreError(nil, fmt.Sprintf("Invalid seek value %v of type %T.", val, t.String())) 128 } 129 retVal := val.(string) 130 op := (incl & side) > 0 131 if side == datastore.HIGH { 132 if op { 133 return retVal, lessOrEqual, true, nil 134 } else { 135 return retVal, less, true, nil 136 } 137 } else { 138 if op { 139 return retVal, greaterOrEqual, true, nil 140 } else { 141 return retVal, greater, true, nil 142 } 143 } 144} 145 146func equals(val, key string) bool { 147 return key == val 148} 149 150func less(top, key string) bool { 151 return key < top 152} 153 154func lessOrEqual(top, key string) bool { 155 return key <= top 156} 157 158func greater(bottom, key string) bool { 159 return key > bottom 160} 161 162func greaterOrEqual(bottom, key string) bool { 163 return key >= bottom 164} 165 166func noop(val, key string) bool { 167 return true 168} 169 170// Credentials 171 172// Return the credentials presented in the context. 173// The second parameter is the ns-server-auth-token value, from the original request, 174// if one is present, else the empty string. 175func credsFromContext(context datastore.QueryContext) (distributed.Creds, string) { 176 credentials := context.Credentials() 177 creds := make(distributed.Creds, len(credentials)) 178 for k, v := range credentials { 179 creds[k] = v 180 } 181 authToken := "" 182 req := context.OriginalHttpRequest() 183 if req != nil && req.Header.Get("ns-server-ui") == "yes" { 184 authToken = req.Header.Get("ns-server-auth-token") 185 } 186 return creds, authToken 187} 188