@@ -1305,12 +1305,6 @@ Table::from_rows(
13051305 const auto & psp_pkey_col = data_table.get_column (" psp_pkey" );
13061306 const auto & psp_okey_col = data_table.get_column (" psp_okey" );
13071307
1308- // rapidjson::StringBuffer buffer;
1309- // buffer.Clear();
1310- // rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
1311- // document.Accept(writer);
1312- // std::cout << buffer.GetString() << std::endl;
1313-
13141308 // 3.) Fill table
13151309 for (const auto & row : document.GetArray ()) {
13161310 for (const auto & it : row.GetObject ()) {
@@ -1352,6 +1346,260 @@ Table::from_rows(
13521346 return tbl;
13531347}
13541348
1349+ void
1350+ Table::update_ndjson (const std::string_view& data, std::uint32_t port_id) {
1351+ rapidjson::Document document;
1352+ rapidjson::StringStream s (data.data ());
1353+ document.ParseStream <rapidjson::kParseStopWhenDoneFlag >(s);
1354+ if (document.Size () == 0 ) {
1355+ return ;
1356+ }
1357+
1358+ if (!document.IsObject ()) {
1359+ // TODO Legacy error message
1360+ PSP_COMPLAIN_AND_ABORT (
1361+ " Cannot determine data types without column names!\n "
1362+ )
1363+ }
1364+
1365+ bool is_implicit = m_index.empty ();
1366+ t_schema table_schema = get_schema ();
1367+
1368+ // 2.) Create table
1369+ t_data_table data_table (table_schema);
1370+ data_table.init ();
1371+
1372+ // 2a.) Estimate row size to reduce malloc pressure.
1373+ auto newlines = 0 ;
1374+ for (char i : data) {
1375+ if (i == ' \n ' ) {
1376+ newlines++;
1377+ }
1378+ }
1379+
1380+ data_table.reserve (newlines + 1 );
1381+ if (is_implicit) {
1382+ data_table.add_column (" psp_pkey" , DTYPE_INT32 , true );
1383+ } else {
1384+ data_table.add_column (
1385+ " psp_pkey" , table_schema.get_dtype (m_index), true
1386+ );
1387+ }
1388+
1389+ t_uindex ii = 0 ;
1390+ const auto & psp_pkey_col = data_table.get_column (" psp_pkey" );
1391+ auto schema = data_table.get_schema ();
1392+ bool is_first_row = true ;
1393+ std::vector<std::string> missing_columns = m_column_names;
1394+
1395+ // 3.) Fill table
1396+ bool is_finished = false ;
1397+ while (!is_finished) {
1398+ if (is_implicit) {
1399+ psp_pkey_col->set_nth <std::uint32_t >(ii, (ii + m_offset) % m_limit);
1400+ }
1401+
1402+ for (const auto & it : document.GetObject ()) {
1403+ std::shared_ptr<t_column> col;
1404+ std::string_view col_name = it.name .GetString ();
1405+ if (std::string_view{it.name .GetString ()} == " __INDEX__" ) {
1406+ col_name = " psp_pkey" ;
1407+ }
1408+
1409+ if (!schema.has_column (col_name)) {
1410+ LOG_DEBUG (" Ignoring column " << col_name);
1411+ LOG_DEBUG (" Schema:\n " << schema);
1412+ continue ;
1413+ }
1414+
1415+ if (is_first_row) {
1416+ missing_columns.erase (
1417+ std ::remove (
1418+ missing_columns.begin (), missing_columns.end (), col_name
1419+ ),
1420+ missing_columns.end ()
1421+ );
1422+ }
1423+
1424+ col = data_table.get_column (col_name);
1425+ auto promote = fill_column_json (col, ii, it.value , true );
1426+ if (promote) {
1427+ std::stringstream ss;
1428+ ss << " Cannot append value of type " << dtype_to_str (*promote)
1429+ << " to column of type " << dtype_to_str (col->get_dtype ())
1430+ << std::endl;
1431+ PSP_COMPLAIN_AND_ABORT (ss.str ());
1432+ }
1433+
1434+ if (!is_implicit && m_index == it.name .GetString ()) {
1435+ fill_column_json (psp_pkey_col, ii, it.value , true );
1436+ }
1437+ }
1438+
1439+ is_first_row = false ;
1440+ if (ii + m_offset >= m_limit) {
1441+ for (auto & col_name : missing_columns) {
1442+ data_table.get_column (col_name)->unset (ii);
1443+ }
1444+ }
1445+
1446+ ii++;
1447+
1448+ document.ParseStream <rapidjson::kParseStopWhenDoneFlag >(s);
1449+ if (document.HasParseError ()) {
1450+ is_finished = true ;
1451+ }
1452+ }
1453+
1454+ data_table.extend (ii);
1455+ data_table.clone_column (" psp_pkey" , " psp_okey" );
1456+ process_op_column (data_table, t_op::OP_INSERT );
1457+ calculate_offset (ii);
1458+ m_pool->send (get_gnode ()->get_id (), port_id, data_table);
1459+ }
1460+
1461+ std::shared_ptr<Table>
1462+ Table::from_ndjson (
1463+ const std::string& index, const std::string_view& data, std::uint32_t limit
1464+ ) {
1465+ auto pool = std::make_shared<t_pool>();
1466+ pool->init ();
1467+
1468+ // 1.) Infer schema
1469+ rapidjson::Document document;
1470+ rapidjson::StringStream s (data.data ());
1471+ document.ParseStream <rapidjson::kParseStopWhenDoneFlag >(s);
1472+
1473+ if (document.Size () > 0 && !document.IsObject ()) {
1474+ std::stringstream ss;
1475+ ss << " Received non-object " << document[0 ].GetType ();
1476+ PSP_COMPLAIN_AND_ABORT (ss.str ())
1477+ }
1478+
1479+ std::vector<std::string> column_names;
1480+ std::vector<t_dtype> data_types;
1481+ bool is_implicit = true ;
1482+ std::set<std::string> columns_known_type;
1483+ std::set<std::string> columns_seen;
1484+
1485+ // TODO I don't think it makes sense to do the same incremental-schema
1486+ // enhancement we do for regular JSON. For now this only checks the first
1487+ // row.
1488+ [&]() {
1489+ for (const auto & col : document.GetObject ()) {
1490+ columns_seen.insert (col.name .GetString ());
1491+ }
1492+
1493+ // https://github.com/Tencent/rapidjson/issues/1994
1494+ for (const auto & col : document.GetObject ()) {
1495+ if (col.name .GetString () == index) {
1496+ is_implicit = false ;
1497+ }
1498+
1499+ if (columns_known_type.count (col.name .GetString ()) > 0 ) {
1500+ continue ;
1501+ }
1502+
1503+ auto dtype = rapidjson_type_to_dtype (col.value );
1504+ if (dtype != DTYPE_NONE ) {
1505+ columns_known_type.insert (col.name .GetString ());
1506+ data_types.push_back (rapidjson_type_to_dtype (col.value ));
1507+ column_names.emplace_back (col.name .GetString ());
1508+ }
1509+
1510+ // Theoretically there can end too early if the first
1511+ // few rows are missing columns that are present in later rows.
1512+ if (columns_known_type.size () == columns_seen.size ()) {
1513+ return ;
1514+ }
1515+ }
1516+ }();
1517+
1518+ auto untyped_columns = columns_seen;
1519+ for (const auto & col : columns_seen) {
1520+ if (columns_known_type.count (col) == 0 ) {
1521+ // Default all null columns to string
1522+ data_types.push_back (DTYPE_STR );
1523+ column_names.emplace_back (col);
1524+ }
1525+ }
1526+
1527+ t_schema schema (column_names, data_types);
1528+
1529+ // 2.) Create table
1530+ t_data_table data_table (schema);
1531+ data_table.init ();
1532+
1533+ if (is_implicit) {
1534+ data_table.add_column (" psp_pkey" , DTYPE_INT32 , true );
1535+ data_table.add_column (" psp_okey" , DTYPE_INT32 , true );
1536+ } else {
1537+ data_table.add_column (" psp_pkey" , schema.get_dtype (index), true );
1538+ data_table.add_column (" psp_okey" , schema.get_dtype (index), true );
1539+ }
1540+
1541+ std::int32_t ii = 0 ;
1542+ const auto & psp_pkey_col = data_table.get_column (" psp_pkey" );
1543+ const auto & psp_okey_col = data_table.get_column (" psp_okey" );
1544+
1545+ // 2a.) Estimate row size to reduce malloc pressure.
1546+ auto newlines = 0 ;
1547+ for (char i : data) {
1548+ if (i == ' \n ' ) {
1549+ newlines++;
1550+ }
1551+ }
1552+
1553+ data_table.reserve (newlines + 1 );
1554+
1555+ // 3.) Fill table
1556+ bool is_finished = false ;
1557+ while (!is_finished) {
1558+ for (const auto & it : document.GetObject ()) {
1559+ auto col = data_table.get_column (it.name .GetString ());
1560+ const auto * col_name = it.name .GetString ();
1561+ const auto & cell = it.value ;
1562+ auto promote = fill_column_json (col, ii, cell, false );
1563+ if (promote) {
1564+ LOG_DEBUG (
1565+ " Promoting column " << col_name << " from "
1566+ << dtype_to_str (col->get_dtype ())
1567+ << " to " << dtype_to_str (*promote)
1568+ );
1569+
1570+ data_table.promote_column (col_name, *promote, ii, true );
1571+ col = data_table.get_column (col_name);
1572+ fill_column_json (col, ii, cell, false );
1573+ }
1574+
1575+ if (!is_implicit && index == it.name .GetString ()) {
1576+ fill_column_json (psp_pkey_col, ii, it.value , false );
1577+ fill_column_json (psp_okey_col, ii, it.value , false );
1578+ }
1579+ }
1580+
1581+ if (is_implicit) {
1582+ psp_pkey_col->set_nth <std::int32_t >(ii, ii % limit);
1583+ psp_okey_col->set_nth <std::int32_t >(ii, ii % limit);
1584+ }
1585+
1586+ ii++;
1587+ document.ParseStream <rapidjson::kParseStopWhenDoneFlag >(s);
1588+ if (document.HasParseError ()) {
1589+ is_finished = true ;
1590+ }
1591+ }
1592+
1593+ data_table.extend (ii);
1594+ auto tbl = std::make_shared<Table>(
1595+ pool, schema.columns (), schema.types (), limit, index
1596+ );
1597+
1598+ tbl->init (data_table, ii, t_op::OP_INSERT , 0 );
1599+ pool->_process ();
1600+ return tbl;
1601+ }
1602+
13551603std::shared_ptr<Table>
13561604Table::from_schema (
13571605 const std::string& index, const t_schema& schema, std::uint32_t limit
0 commit comments