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 = ¶ms->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