1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2015-2017 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include <libcouchbase/couchbase.h>
19 #include <libcouchbase/n1ql.h>
20 #include <libcouchbase/vbucket.h>
21 #include "contrib/lcb-jsoncpp/lcb-jsoncpp.h"
22 #include <string>
23 
24 #define SCANVEC_NONE 0
25 #define SCANVEC_PARTIAL 1
26 #define SCANVEC_FULL 2
27 
28 struct lcb_N1QLPARAMS_st {
29     Json::Value root;
30     std::string encoded;
31 
lcb_N1QLPARAMS_stlcb_N1QLPARAMS_st32     lcb_N1QLPARAMS_st() : root(Json::objectValue) {
33     }
34 };
35 
36 extern "C" {
get_strlen(const char * s,size_t n)37 static size_t get_strlen(const char *s, size_t n)
38 {
39     if (n == (size_t)-1) {
40         return strlen(s);
41     } else {
42         return n;
43     }
44 }
45 
46 static lcb_error_t
setopt(lcb_N1QLPARAMS * params,const char * k,size_t nk,const char * v,size_t nv)47 setopt(lcb_N1QLPARAMS *params, const char *k, size_t nk, const char *v, size_t nv)
48 {
49     nv = get_strlen(v, nv);
50     nk = get_strlen(k, nk);
51     Json::Reader rdr;
52     Json::Value value;
53     bool rv = rdr.parse(v, v + nv, value);
54     if (!rv) {
55         return LCB_EINVAL;
56     }
57 
58     params->root[std::string(k, nk)] = value;
59     return LCB_SUCCESS;
60 }
61 
62 lcb_error_t
lcb_n1p_setopt(lcb_N1QLPARAMS * params,const char * k,size_t nk,const char * v,size_t nv)63 lcb_n1p_setopt(lcb_N1QLPARAMS *params,
64     const char *k, size_t nk, const char *v, size_t nv)
65 {
66     return setopt(params, k, nk, v, nv);
67 }
68 
69 lcb_error_t
lcb_n1p_setquery(lcb_N1QLPARAMS * params,const char * qstr,size_t nqstr,int type)70 lcb_n1p_setquery(lcb_N1QLPARAMS *params,
71     const char *qstr, size_t nqstr, int type)
72 {
73     if (type == LCB_N1P_QUERY_STATEMENT) {
74         size_t nstmt = get_strlen(qstr, nqstr);
75         params->root["statement"] = std::string(qstr, nstmt);
76         return LCB_SUCCESS;
77     } else if (type == LCB_N1P_QUERY_PREPARED) {
78         return lcb_n1p_setopt(params, "prepared", -1, qstr, nqstr);
79     } else {
80         return LCB_EINVAL;
81     }
82 }
83 
84 lcb_error_t
lcb_n1p_namedparam(lcb_N1QLPARAMS * params,const char * name,size_t nname,const char * value,size_t nvalue)85 lcb_n1p_namedparam(lcb_N1QLPARAMS *params,
86     const char *name, size_t nname, const char *value, size_t nvalue)
87 {
88     return lcb_n1p_setopt(params, name, nname, value, nvalue);
89 }
90 
91 lcb_error_t
lcb_n1p_posparam(lcb_N1QLPARAMS * params,const char * value,size_t nvalue)92 lcb_n1p_posparam(lcb_N1QLPARAMS *params, const char *value, size_t nvalue)
93 {
94     nvalue = get_strlen(value, nvalue);
95     Json::Value jval;
96     Json::Reader rdr;
97     if (!rdr.parse(value, value + nvalue, jval)) {
98         return LCB_EINVAL;
99     }
100     params->root["args"].append(jval);
101     return LCB_SUCCESS;
102 }
103 
104 lcb_error_t
lcb_n1p_readonly(lcb_N1QLPARAMS * params,int readonly)105 lcb_n1p_readonly(lcb_N1QLPARAMS *params, int readonly)
106 {
107     params->root["readonly"] = readonly ? true : false;
108     return LCB_SUCCESS;
109 }
110 
111 lcb_error_t
lcb_n1p_scancap(lcb_N1QLPARAMS * params,int scancap)112 lcb_n1p_scancap(lcb_N1QLPARAMS *params, int scancap)
113 {
114     params->root["scan_cap"] = Json::valueToString(scancap);
115     return LCB_SUCCESS;
116 }
117 
118 lcb_error_t
lcb_n1p_pipelinecap(lcb_N1QLPARAMS * params,int pipelinecap)119 lcb_n1p_pipelinecap(lcb_N1QLPARAMS *params, int pipelinecap)
120 {
121     params->root["pipeline_cap"] = Json::valueToString(pipelinecap);
122     return LCB_SUCCESS;
123 }
124 
125 lcb_error_t
lcb_n1p_pipelinebatch(lcb_N1QLPARAMS * params,int pipelinebatch)126 lcb_n1p_pipelinebatch(lcb_N1QLPARAMS *params, int pipelinebatch)
127 {
128     params->root["pipeline_batch"] = Json::valueToString(pipelinebatch);
129     return LCB_SUCCESS;
130 }
131 
132 static void
encode_mutation_token(Json::Value & sparse,const lcb_MUTATION_TOKEN * sv)133 encode_mutation_token(Json::Value& sparse, const lcb_MUTATION_TOKEN *sv)
134 {
135     char buf[64] = { 0 };
136     sprintf(buf, "%u", sv->vbid_);
137     Json::Value& cur_sv = sparse[buf];
138 
139     cur_sv[0] = static_cast<Json::UInt64>(sv->seqno_);
140     sprintf(buf, "%llu", (unsigned long long)sv->uuid_);
141     cur_sv[1] = buf;
142 }
143 
144 lcb_error_t
lcb_n1p_setconsistent_token(lcb_N1QLPARAMS * params,const char * keyspace,const lcb_MUTATION_TOKEN * sv)145 lcb_n1p_setconsistent_token(lcb_N1QLPARAMS *params,
146     const char *keyspace, const lcb_MUTATION_TOKEN *sv)
147 {
148     if (!LCB_MUTATION_TOKEN_ISVALID(sv)) {
149         return LCB_EINVAL;
150     }
151 
152     params->root["scan_consistency"] = "at_plus";
153     encode_mutation_token(params->root["scan_vectors"][keyspace], sv);
154     return LCB_SUCCESS;
155 }
156 
157 lcb_error_t
lcb_n1p_setconsistent_handle(lcb_N1QLPARAMS * params,lcb_t instance)158 lcb_n1p_setconsistent_handle(lcb_N1QLPARAMS *params, lcb_t instance)
159 {
160     lcbvb_CONFIG *vbc;
161     lcb_error_t rc = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, &vbc);
162     if (rc != LCB_SUCCESS) {
163         return rc;
164     }
165 
166     const char *bucketname;
167     rc = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_BUCKETNAME, &bucketname);
168     if (rc != LCB_SUCCESS) {
169         return rc;
170     }
171 
172     Json::Value* sv_json = NULL;
173 
174     size_t vbmax = vbc->nvb;
175     for (size_t ii = 0; ii < vbmax; ++ii) {
176         lcb_KEYBUF kb;
177         kb.type = LCB_KV_VBID;
178         kb.contig.nbytes = ii;
179         kb.contig.bytes = NULL;
180         const lcb_MUTATION_TOKEN *mt = lcb_get_mutation_token(instance, &kb, &rc);
181         if (rc == LCB_SUCCESS && mt != NULL) {
182             if (sv_json == NULL) {
183                 sv_json = &params->root["scan_vectors"][bucketname];
184                 params->root["scan_consistency"] = "at_plus";
185             }
186             encode_mutation_token(*sv_json, mt);
187         }
188     }
189 
190     if (!sv_json) {
191         return LCB_KEY_ENOENT;
192     }
193 
194     return LCB_SUCCESS;
195 }
196 
197 lcb_error_t
lcb_n1p_setconsistency(lcb_N1QLPARAMS * params,int mode)198 lcb_n1p_setconsistency(lcb_N1QLPARAMS *params, int mode)
199 {
200     if (mode == LCB_N1P_CONSISTENCY_NONE) {
201         params->root.removeMember("scan_consistency");
202     } else if (mode == LCB_N1P_CONSISTENCY_REQUEST) {
203         params->root["scan_consistency"] = "request_plus";
204     } else if (mode == LCB_N1P_CONSISTENCY_STATEMENT) {
205         params->root["scan_consistency"] = "statement_plus";
206     }
207     return LCB_SUCCESS;
208 }
209 
210 LIBCOUCHBASE_API
211 const char *
lcb_n1p_encode(lcb_N1QLPARAMS * params,lcb_error_t * err)212 lcb_n1p_encode(lcb_N1QLPARAMS *params, lcb_error_t *err)
213 {
214     lcb_error_t err_s = LCB_SUCCESS;
215     if (!err) {
216         err = &err_s;
217     }
218 
219     *err = LCB_SUCCESS;
220     /* Build the query */
221     Json::FastWriter w;
222     params->encoded = w.write(params->root);
223     return params->encoded.c_str();
224 }
225 
226 LIBCOUCHBASE_API
227 lcb_error_t
lcb_n1p_mkcmd(lcb_N1QLPARAMS * params,lcb_CMDN1QL * cmd)228 lcb_n1p_mkcmd(lcb_N1QLPARAMS *params, lcb_CMDN1QL *cmd)
229 {
230     lcb_error_t rc = LCB_SUCCESS;
231     lcb_n1p_encode(params, &rc);
232     if (rc != LCB_SUCCESS) {
233         return rc;
234     }
235     cmd->content_type = "application/json";
236     cmd->query = params->encoded.c_str();
237     cmd->nquery = params->encoded.size();
238     return LCB_SUCCESS;
239 }
240 
241 lcb_N1QLPARAMS *
lcb_n1p_new(void)242 lcb_n1p_new(void)
243 {
244     return new lcb_N1QLPARAMS;
245 }
246 
247 void
lcb_n1p_reset(lcb_N1QLPARAMS * params)248 lcb_n1p_reset(lcb_N1QLPARAMS *params)
249 {
250     params->encoded.clear();
251     params->root.clear();
252 }
253 
254 void
lcb_n1p_free(lcb_N1QLPARAMS * params)255 lcb_n1p_free(lcb_N1QLPARAMS *params)
256 {
257     delete params;
258 }
259 
260 } // extern C
261