Skip to content

Commit b979f89

Browse files
committed
Bug 27677910: Fix ReplaceOne(expr()).
1 parent 80c7195 commit b979f89

File tree

3 files changed

+195
-22
lines changed

3 files changed

+195
-22
lines changed

devapi/crud.cc

Lines changed: 176 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,187 @@ struct Upsert_cmd : public Executable<Result, Upsert_cmd>
142142
};
143143

144144

145+
struct Value_expr_check_id
146+
: cdk::Expression
147+
, cdk::Expression::Processor
148+
, cdk::Expression::Processor::Doc_prc
149+
{
150+
Value_expr &m_expr;
151+
bool m_is_expr;
152+
153+
Processor *m_prc;
154+
Doc_prc *m_doc_prc;
155+
156+
struct Any_processor_check
157+
: cdk::Expression::Processor::Doc_prc::Any_prc
158+
, cdk::Expression::Processor::Doc_prc::Any_prc::Scalar_prc
159+
, cdk::Expression::Processor::Doc_prc::Any_prc::Scalar_prc::Value_prc
160+
{
161+
Any_prc *m_id_prc;
162+
Scalar_prc *m_scalar_prc;
163+
Value_prc *m_value_prc;
164+
const string& m_id;
165+
166+
Any_processor_check(const string& id)
167+
: m_id(id)
168+
{}
169+
170+
// Any processor implementation
171+
172+
Scalar_prc* scalar() override
173+
{
174+
m_scalar_prc = m_id_prc->scalar();
175+
return m_scalar_prc ? this : nullptr;
176+
}
177+
178+
List_prc* arr() override
179+
{
180+
return m_id_prc->arr();
181+
}
182+
183+
Doc_prc* doc() override
184+
{
185+
return m_id_prc->doc();
186+
}
187+
188+
//Scalar processor implementation
189+
190+
Value_prc* val() override
191+
{
192+
m_value_prc = m_scalar_prc->val();
193+
return m_value_prc ? this : nullptr;
194+
}
195+
196+
Args_prc* op(const char *name) override
197+
{
198+
return m_scalar_prc->op(name);
199+
}
200+
Args_prc* call(const Object_ref&obj) override
201+
{
202+
return m_scalar_prc->call(obj);
203+
}
204+
205+
void ref(const Column_ref &col, const Doc_path *path) override
206+
{
207+
return m_scalar_prc->ref(col, path);
208+
}
209+
void ref(const Doc_path &path) override
210+
{
211+
return m_scalar_prc->ref(path);
212+
}
213+
214+
void param(const string &val) override
215+
{
216+
return m_scalar_prc->param(val);
217+
}
218+
219+
void param(uint16_t val) override
220+
{
221+
return m_scalar_prc->param(val);
222+
}
223+
224+
void var(const string &name) override
225+
{
226+
m_scalar_prc->var(name);
227+
}
228+
229+
// Value processor implementation
230+
virtual void null() override { m_value_prc->null();}
231+
232+
virtual void value(cdk::Type_info type,
233+
const cdk::Format_info &format,
234+
cdk::foundation::bytes val) override
235+
{
236+
m_value_prc->value(type, format, val);
237+
}
238+
239+
virtual void str(const string &val)
240+
{
241+
if (m_id != val)
242+
throw Error(R"(Document "_id" and replace id are different!)");
243+
m_value_prc->str(val);
244+
}
245+
virtual void num(int64_t val) { m_value_prc->num(val); }
246+
virtual void num(uint64_t val) { m_value_prc->num(val); }
247+
virtual void num(float val) { m_value_prc->num(val); }
248+
virtual void num(double val) { m_value_prc->num(val); }
249+
virtual void yesno(bool val) { m_value_prc->yesno(val); }
250+
251+
};
252+
253+
Any_processor_check m_any_prc;
254+
255+
Value_expr_check_id(Value_expr &expr, bool is_expr, const string& id)
256+
: m_expr(expr)
257+
, m_is_expr(is_expr)
258+
, m_any_prc(id)
259+
{}
260+
261+
// Expression implementation
262+
263+
void process(Processor& prc) const override
264+
{
265+
auto self = const_cast<Value_expr_check_id*>(this);
266+
self->m_prc = &prc;
267+
m_expr.process(*self);
268+
}
269+
270+
// Expression processor implementation
271+
272+
Scalar_prc* scalar() override
273+
{
274+
return m_prc->scalar();
275+
}
276+
277+
List_prc* arr() override
278+
{
279+
return m_prc->arr();
280+
}
281+
282+
Doc_prc* doc() override
283+
{
284+
m_doc_prc = m_prc->doc();
285+
return m_doc_prc ? this : nullptr;
286+
}
287+
288+
// Doc_prc implementation
289+
290+
void doc_begin() override
291+
{
292+
m_doc_prc->doc_begin();
293+
}
294+
295+
void doc_end() override
296+
{
297+
m_doc_prc->doc_end();
298+
}
299+
300+
Any_prc* key_val(const string &key) override
301+
{
302+
if (string("_id") == key )
303+
{
304+
if (m_is_expr)
305+
throw Error(R"(Document "_id" will be replaced by expression "_id")");
306+
307+
m_any_prc.m_id_prc = m_doc_prc->key_val(key);
308+
return m_any_prc.m_id_prc ? &m_any_prc : nullptr;
309+
}
310+
return m_doc_prc->key_val(key);
311+
}
312+
313+
};
314+
315+
145316
Result
146317
internal::Collection_detail::add_or_replace_one(
147318
const string &id, Value &&doc, bool replace
148319
)
149320
{
150321
Object_ref coll(get_schema().m_name, m_name);
151322

152-
if (doc.getType() == Value::STRING)
323+
324+
if (!Value::Access::is_expr(doc) &&
325+
doc.getType() == Value::STRING)
153326
{
154327
doc = DbDoc(doc.get<string>());
155328
}
@@ -158,22 +331,9 @@ internal::Collection_detail::add_or_replace_one(
158331

159332
if (replace)
160333
{
161-
string doc_id;
162-
bool has_id = true;
163-
164-
try {
165-
doc_id = doc.get<DbDoc>()["_id"];
166-
} catch (std::out_of_range)
167-
{
168-
has_id = false;
169-
}
170-
171-
if (has_id && (doc_id != id))
172-
{
173-
throw Error(R"(Document "_id" and replace id are different!)");
174-
}
334+
Value_expr_check_id check_id(expr, Value::Access::is_expr(doc), id);
175335

176-
Replace_cmd cmd(m_sess, coll, std::string(id), expr);
336+
Replace_cmd cmd(m_sess, coll, std::string(id), check_id);
177337
return cmd.execute();
178338
}
179339
else

devapi/impl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ namespace mysqlx {
5757

5858
struct Value::Access
5959
{
60+
61+
/*
62+
Check if Value is an expression
63+
*/
64+
65+
static bool is_expr(const Value &val)
66+
{
67+
return val.is_expr();
68+
}
69+
6070
/*
6171
Build document value from a JSON string which is
6272
assumed to describe a document.

devapi/tests/crud-t.cc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2251,9 +2251,9 @@ TEST_F(Crud, single_document)
22512251

22522252
coll.remove("true").execute();
22532253

2254-
coll.add("{\"_id\":\"id1\", \"name\":\"foo\" }" )
2255-
.add("{\"_id\":\"id2\", \"name\":\"bar\" }" )
2256-
.add("{\"_id\":\"id3\", \"name\":\"baz\" }" )
2254+
coll.add(R"({"_id":"id1", "name":"foo", "age": 1 })" )
2255+
.add(R"({"_id":"id2", "name":"bar", "age": 2 })" )
2256+
.add(R"({"_id":"id3", "name":"baz", "age": 3 })" )
22572257
.execute();
22582258

22592259
EXPECT_EQ(string("foo"), coll.getOne("id1")["name"].get<string>());
@@ -2266,9 +2266,12 @@ TEST_F(Crud, single_document)
22662266
EXPECT_TRUE(coll.getOne("id1").isNull());
22672267

22682268
// Replace existing document
2269-
EXPECT_EQ(1, coll.replaceOne("id3", expr("{\"name\": \"qux\" }"))
2270-
.getAffectedItemsCount());
2269+
EXPECT_EQ(1, coll.replaceOne(
2270+
"id3", expr(R"({"name": "qux",
2271+
"age": cast(age+1 AS UNSIGNED INT) })"))
2272+
.getAffectedItemsCount());
22712273
EXPECT_EQ(string("qux"), coll.getOne("id3")["name"].get<string>());
2274+
EXPECT_EQ(4, coll.getOne("id3")["age"].get<int>());
22722275

22732276
// Setting a different _id on document should throw error
22742277
// Document passed as string
@@ -2290,7 +2293,7 @@ TEST_F(Crud, single_document)
22902293

22912294
// should affect none
22922295
EXPECT_EQ(0,
2293-
coll.replaceOne("id4", expr("{\"_id\":\"id4\", \"name\": \"baz\" }"))
2296+
coll.replaceOne("id4", expr(R"({"name": "baz" })"))
22942297
.getAffectedItemsCount());
22952298
}
22962299

0 commit comments

Comments
 (0)