@@ -30,6 +30,9 @@ extern "C"
3030#include < lualib.h>
3131}
3232
33+ #include < rapidjson/stringbuffer.h>
34+ #include < rapidjson/writer.h>
35+
3336#include < cassert>
3437#include < cstdio>
3538#include < cstdlib>
@@ -318,6 +321,122 @@ static void write_double(db_copy_mgr_t<db_deleter_by_type_and_id_t> *copy_mgr,
318321 copy_mgr->add_column (value);
319322}
320323
324+ using json_writer_type = rapidjson::Writer<rapidjson::StringBuffer>;
325+ using table_register_type = std::vector<void const *>;
326+
327+ /* *
328+ * Check that the value on the top of the Lua stack is a simple array.
329+ * This means that all keys must be consecutive integers starting from 1.
330+ */
331+ static bool is_lua_array (lua_State *lua_state)
332+ {
333+ uint32_t n = 1 ;
334+ lua_pushnil (lua_state);
335+ while (lua_next (lua_state, -2 ) != 0 ) {
336+ lua_pop (lua_state, 1 ); // remove value from stack
337+ if (!lua_isinteger (lua_state, -1 )) {
338+ lua_pop (lua_state, 1 );
339+ return false ;
340+ }
341+ int okay = 0 ;
342+ auto const num = lua_tointegerx (lua_state, -1 , &okay);
343+ if (!okay || num != n++) {
344+ lua_pop (lua_state, 1 );
345+ return false ;
346+ }
347+ }
348+
349+ // An empty lua table could be both, we decide here that it is not stored
350+ // as a JSON array but as a JSON object.
351+ if (n == 1 ) {
352+ return false ;
353+ }
354+
355+ return true ;
356+ }
357+
358+ static void write_json (json_writer_type *writer, lua_State *lua_state,
359+ table_register_type *tables);
360+
361+ static void write_json_table (json_writer_type *writer, lua_State *lua_state,
362+ table_register_type *tables)
363+ {
364+ void const *table_ptr = lua_topointer (lua_state, -1 );
365+ assert (table_ptr);
366+ auto const it = std::find (tables->cbegin (), tables->cend (), table_ptr);
367+ if (it != tables->cend ()) {
368+ throw std::runtime_error{" Loop detected in table" };
369+ }
370+ tables->push_back (table_ptr);
371+
372+ if (is_lua_array (lua_state)) {
373+ writer->StartArray ();
374+ lua_pushnil (lua_state);
375+ while (lua_next (lua_state, -2 ) != 0 ) {
376+ write_json (writer, lua_state, tables);
377+ lua_pop (lua_state, 1 );
378+ }
379+ writer->EndArray ();
380+ } else {
381+ writer->StartObject ();
382+ lua_pushnil (lua_state);
383+ while (lua_next (lua_state, -2 ) != 0 ) {
384+ int const ltype_key = lua_type (lua_state, -2 );
385+ if (ltype_key != LUA_TSTRING) {
386+ throw std::runtime_error{
387+ " Incorrect data type '{}' as key." _format (
388+ lua_typename (lua_state, ltype_key))};
389+ }
390+ char const *const key = lua_tostring (lua_state, -2 );
391+ writer->Key (key);
392+ write_json (writer, lua_state, tables);
393+ lua_pop (lua_state, 1 );
394+ }
395+ writer->EndObject ();
396+ }
397+ }
398+
399+ static void write_json_number (json_writer_type *writer, lua_State *lua_state)
400+ {
401+ int okay = 0 ;
402+ auto const num = lua_tointegerx (lua_state, -1 , &okay);
403+ if (okay) {
404+ writer->Int64 (num);
405+ } else {
406+ writer->Double (lua_tonumber (lua_state, -1 ));
407+ }
408+ }
409+
410+ static void write_json (json_writer_type *writer, lua_State *lua_state,
411+ table_register_type *tables)
412+ {
413+ assert (writer);
414+ assert (lua_state);
415+
416+ int const ltype = lua_type (lua_state, -1 );
417+ switch (ltype) {
418+ case LUA_TNIL:
419+ writer->Null ();
420+ break ;
421+ case LUA_TBOOLEAN:
422+ writer->Bool (lua_toboolean (lua_state, -1 ) != 0 );
423+ break ;
424+ case LUA_TNUMBER:
425+ write_json_number (writer, lua_state);
426+ break ;
427+ case LUA_TSTRING:
428+ writer->String (lua_tostring (lua_state, -1 ));
429+ break ;
430+ case LUA_TTABLE:
431+ write_json_table (writer, lua_state, tables);
432+ break ;
433+ default :
434+ throw std::runtime_error{
435+ " Invalid type '{}' for json/jsonb column." _format (
436+ lua_typename (lua_state, ltype))};
437+ }
438+ }
439+
321440void output_flex_t::write_column (
322441 db_copy_mgr_t <db_deleter_by_type_and_id_t > *copy_mgr,
323442 flex_table_column_t const &column)
@@ -467,6 +586,13 @@ void output_flex_t::write_column(
467586 " Invalid type '{}' for hstore column." _format (
468587 lua_typename (lua_state (), ltype))};
469588 }
589+ } else if ((column.type () == table_column_type::json) ||
590+ (column.type () == table_column_type::jsonb)) {
591+ rapidjson::StringBuffer stream;
592+ json_writer_type writer{stream};
593+ table_register_type tables;
594+ write_json (&writer, lua_state (), &tables);
595+ copy_mgr->add_column (stream.GetString ());
470596 } else if (column.type () == table_column_type::direction) {
471597 switch (ltype) {
472598 case LUA_TBOOLEAN:
0 commit comments