Libelektra: YAJL: Plugin Does Not Save Value Directly Below Mountpoint

Created on 27 Jul 2018  ·  17Comments  ·  Source: ElektraInitiative/libelektra

Steps to Reproduce the Problem

kdb mount config.json user/tests/yajl yajl
kdb set user/tests/yajl 'This May Be the Year I Disappear'
kdb ls user/tests/yajl
#> user/tests/yajl
kdb get user/tests/yajl 

Expected Result

The last command should print the text This May Be the Year I Disappear.

Actual Result

The last command prints an empty line.

System Information

  • Elektra Version: master
  • OS: macOS 10.13.6
bug good first issue usability

All 17 comments

Thank you for reporting the problem!

How would such a JSON document look like? We also need documentation for the special semantics of configuration files only containing the parent key.

How would such a JSON document look like?

The document would only contain a string in this case:

"This May Be the Year I Disappear"

. The plugin already reads the correct value, if I replace the configuration file with the content above:

printf '"This May Be the Year I Disappear"' > (kdb file user/tests/yajl/)
kdb get user/tests/yajl
#> This May Be the Year I Disappear

.

So only set needs to be fixed? This sounds quite easy!

So only set needs to be fixed?

As far as I can tell, yes.

I think I can look into this as my first issue. Can you give me a few pointers where I should start to look @markus2330? Thanks.

The source is in src/plugins/yajl/yajl_gen.c and src/plugins/yajl/yajl_parse.c

Luckily @sanssecours wrote an excellent tutorial describing how the storage plugins should behave (see doc/tutorials/storage-plugins.md). So you could take as next issue #2132 (#2477 is maybe a result of this) and other yajl related problems. Your main work could be the validation of arrays in the yajl plugin or -- even better -- as external plugin (see #1862).

If I mount an empty json document (meaning it contains just {}), then the following happens when I call kdb set system/lvas/cm/yajl "test".
elektraYajlSet is called with a KeySet that has jus (system/lvas/cm/yajl,test) as values.

In the call to elektraGenEmpty the second clause is executed, since they KeySets size is 1.
The strcmp also succeeds because the parentKey has the same value as the last key in the Keyset.

An empty map is generated and elektraYajlSet terminates. So the function receives the value, but does not do anything with it.

So at that point where the empty map is generated, I could add code that outputs the keys value into a json document as described by @sanssecours here.
This would work, since kdb get reads that value correctly and a call like kdb set system/lvas/cm/yajl/second "hi" produces a json document like this {"__dirdata": "test", "second": "hi"}, which maps the keys as expected.

I think this solution would work, but I'd find it cleaner if I could set the json document directly to {"__dirdata": "test"}, which would be the same semantically.

But I'm not sure how I would do that. I assume the correct way would be that the directoryvalue plugin would modify the Keyset which elektraYajlSet receives. Or I guess I could modify the KeySet myself in elektraYajlSet.

Do you have a suggestion @markus2330?

Yes, the directoryvalue modifies the KeySet elektraYajlSet receives. This does not happen, however, if only the parentKey is present (as it is not considered to be a "directory" then.) So if you only set the parent key, as you already found out, elektraGenEmpty will be executed and the strcmp succeeds.

But elektraGenEmpty has some assumptions which are now not true anymore (it was written before directoryvalue existed). So other bugs are there, e.g. #2132

I think this solution would work, but I'd find it cleaner if I could set the json document directly to {"__dirdata": "test"}, which would be the same semantically.

Wouldn't {"": "test"} be the minimal document describing a single value? This also seems to be the behavior of the "camel" plugin.

@sanssecours Can you maybe extend the storage-tutorial to also describe parentKeys and "empty keys" (%) (or at least as TODO for now)?

Can you maybe extend the storage-tutorial to also describe parentKeys…

I think the plugin tutorial already describes

  • the value of parent keys (filepath), and
  • that parent keys are used to emit error and warning information

. I do not know anything else that is special about parent keys.

and "empty keys" (%) (or at least as TODO for now)?

Since this is the first time I heard about empty keys, I do not think I am the right person to do that.

I could add code that outputs the keys value into a json document as described by @sanssecours here.

I am not certain that this is the correct way for YAJL to handle the problem any more, since a valid JSON document seems to always require an array or object at the top-level.

I do not know anything else that is special about parent keys.

There is also nothing else special about it except that some configuration file formats do not have a way to simply describe a value without a key.

"empty keys" (%)

Empty keys allow you to map documents like {"root": {"": "something"}}. How do you map this currently to a KeySet?

I am not certain that this is the correct way for YAJL to handle the problem any more, since a valid JSON document seems to always require an array or object at the top-level.

This seems to have changed: https://stackoverflow.com/questions/13318420/is-a-single-string-value-considered-valid-json

In https://www.ietf.org/rfc/rfc7159.txt there are even examples showing that "only values" are allowed. But it seems like yajl does not support this... (yajl 2.1.0-2+b3 yields the error Expected “{” but found “"”)

With the feature from RFC 7159 we could map:

"some value" -> parent = "some value"
{"", "some value"} -> parent/% = "some value"

But we would need to work around yajl then... (the ChangeLog also does not indicate that this feature was added later).

Empty keys allow you to map documents like {"root": {"": "something"}}. How do you map this currently to a KeySet?

Seems like YAML CPP handles this data properly:

kdb mount config.yaml user/tests/yaml yamlcpp
printf '{"root": {"": "something"}}' > "$(kdb file user/tests/yaml)"
kdb ls user/tests/yaml
#> user/tests/yaml/root/%
kdb get user/tests/yaml/root/%
#> something

. I did not adapt the plugin for empty keys though. Just calling setBaseName in a storage plugin, using the correct string value (""), should be enough to support this feature.

Nice to see that this works out-of-the-box without special handling :+1:

@sanssecours Do we now want to support RFC 7159? What do your plugins do if only the parentKey is set?

Do we now want to support RFC 7159?

In my opinion supporting every data type at the top-level makes sense.

What do your plugins do if only the parentKey is set?

They just store the text (without the key):

kdb mount config.yaml user/tests/yaml yamlcpp
kdb set user/tests/yaml value
kdb file user/tests/yaml | xargs cat
#> value

.

I added special handling for the top-level in this commit.
That breaks a test in the yajl module, but before I go and spend time fixing that I wanted to check in first, if that would be an acceptable solution @markus2330?

Just to elaborate a little bit: If the size of the KeySet is 1, then I can be sure that only the top-level key is in it. Then I generate the value of that key. But in that case I have to prevent elektraGenOpenValue from being called, because it would otherwise generate the key as a string.

Seems like YAML CPP handles this data properly:
...

This example works for me with yajl. Am I missing something?

That breaks a test in the yajl module

Which test? (see also below)

I wanted to check in first, if that would be an acceptable solution

It is easier to see if something is an acceptable solution if:

  • you created an PR (which will then run all test cases)
  • describe in a test and with examples which behavior you now want to implement

This example works for me with yajl. Am I missing something?

No, this example only demonstrates that {"", "some value"} is not an ideal way to represent the parentKey's value.

Do we now want to support RFC 7159?
In my opinion supporting every data type at the top-level makes sense.

I fully agree.

They just store the text (without the key):

So let us implement the same with yajl.

It works perfectly for me now!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

markus2330 picture markus2330  ·  3Comments

mpranj picture mpranj  ·  4Comments

markus2330 picture markus2330  ·  3Comments

markus2330 picture markus2330  ·  4Comments

mpranj picture mpranj  ·  3Comments