From 59cbe4babb543ec3a2e950e50a203b0a65bd75c2 Mon Sep 17 00:00:00 2001
From: Robert Butora <robert.butora@inaf.it>
Date: Fri, 17 Jan 2025 16:49:16 +0100
Subject: [PATCH] vlkb-obscore: simplifies syntax of the groups-argument
 (converts to sql internally)

---
 .../vlkb-obscore/src/database/database.cpp    |  21 +-
 .../engine/src/vlkb-obscore/src/main.cpp      | 284 +++++++++++-------
 2 files changed, 185 insertions(+), 120 deletions(-)

diff --git a/data-access/engine/src/vlkb-obscore/src/database/database.cpp b/data-access/engine/src/vlkb-obscore/src/database/database.cpp
index de5370e..eef2785 100644
--- a/data-access/engine/src/vlkb-obscore/src/database/database.cpp
+++ b/data-access/engine/src/vlkb-obscore/src/database/database.cpp
@@ -360,11 +360,14 @@ void database::dbModifyGroups(int sid, const string groups,
    LOG_trace(__func__);
 
    DbConn db(db_uri, db_schema); 
+   Survey surv = db.querySurveyAttributes(sid);
 
-   vector<string> cmdModGroups{
-      "UPDATE obscore SET groups = '{" + groups + "}' WHERE (policy = 'PRIV') AND (obs_publisher_did IN (SELECT CONCAT('" + (obscore_publisher + "?") + "',pubdid) FROM headers WHERE survey_id =" + to_string(sid) + "))"};
+	vector<string> cmdModGroups
+	{
+		"UPDATE obscore SET groups = ARRAY["+ groups +"]::TEXT[] WHERE (policy = 'PRIV') AND (obs_collection = '"+ surv.getObsCollection() +"') AND (obs_title = '"+ surv.getObsTitle()  +"')"
+	};
 
-   db.dbExecCmds(cmdModGroups);
+	db.dbExecCmds(cmdModGroups);
 }
 
 
@@ -372,17 +375,17 @@ void database::dbModifyGroups(int sid, const string groups,
 // FIXME hid identifies rows to remove in tables -> no good, should be pubdid as identificator
 void database::dbRemoveSurvey(int sid, const string db_uri, const string db_schema)
 {
-   LOG_trace(__func__);
+	LOG_trace(__func__);
 
-   DbConn db(db_uri, db_schema);
+	DbConn db(db_uri, db_schema);
 
-   Survey surv = db.querySurveyAttributes(sid);
+	Survey surv = db.querySurveyAttributes(sid);
 
-   SqlSchema_DELETE cmdDelete;
+	SqlSchema_DELETE cmdDelete;
 
-   vector<string> deleteRowsAllTables = {cmdDelete.getCommand(sid, surv)};
+	vector<string> deleteRowsAllTables = {cmdDelete.getCommand(sid, surv)};
 
-   db.dbExecCmds(deleteRowsAllTables);
+	db.dbExecCmds(deleteRowsAllTables);
 }
 
 
diff --git a/data-access/engine/src/vlkb-obscore/src/main.cpp b/data-access/engine/src/vlkb-obscore/src/main.cpp
index 23e8b0c..9e59e05 100644
--- a/data-access/engine/src/vlkb-obscore/src/main.cpp
+++ b/data-access/engine/src/vlkb-obscore/src/main.cpp
@@ -303,77 +303,139 @@ int cmd_dbAdd(int argc, char * argv[])
    return rc;
 }
 
+// convert argv[] bash arg to sql-TEXT[] string
 
-
-int cmd_dbModGroups(int argc, char * argv[])
+// segment fault if str is empty or contains _only_ delimiter(s): "," ",," ",,,,,,,,"
+std::vector<std::string> split_by_delimiter(std::string_view str, std::string_view delimiter)
 {
-   // FIXME groups should be cmd arg and mandatory of policy PRIVATE and empy null if policu PUBLIC
-   // e.g. exit with error if dbAddSurvey gets sid of PRIVATE but no groups arg supplied
-   string groups;
-   int sid_from, sid_to;
-   int rc;
-   switch(argc)
-   {
-      case 3:
-         groups   = std::string(argv[1]);
-         sid_from = std::stoi(argv[2]);
-         sid_to   = sid_from;
-         break;
+   if (delimiter.empty()) return {std::string(str)};
+   // handle empty delimiters explicitly so we can't fall into an infinite loop
 
-      case 4:
-         groups   = std::string(argv[1]);
-         sid_from = std::stoi(argv[2]);
-         sid_to   = std::stoi(argv[3]);
-         break;
-
-      default:
-         cout << "Usage: dbmodgroups <groups> <SID-from> [SID-to]" << endl
-            << "modifies list of groups which can access PRIVATE surveys" << endl;
-         rc = vlkb::EXIT_WITH_USAGE;
-   }
+   std::vector<std::string> tokens;
+   std::size_t              cursor        = 0;
+   std::size_t              segment_start = cursor;
 
-   try
+   while ((cursor = str.find(delimiter, cursor)) != std::string_view::npos)
    {
-      for(int i=sid_from; i<=sid_to; i++)
-         database::dbModifyGroups(i, groups,
-               vlkb::conf.getObsCorePublisher(), vlkb::conf.getDbUri(WITH_PASSWORD), vlkb::conf.getDbSchema());
-      rc = 0;
+      if (segment_start != cursor) tokens.emplace_back(str.substr(segment_start, cursor - segment_start));
+      // don't emplace empty tokens in case of leading/trailing/repeated delimiters
+      cursor += delimiter.size();
+      segment_start = cursor;
    }
-   catch(exception& e)
-   {
-      rc = vlkb::EXIT_WITH_ERROR;
-      ERROR_STREAM << e.what() << endl;
+
+   if (segment_start != str.size()) tokens.emplace_back(str.substr(segment_start));
+   // 'cursor' is now at 'npos', so we compare to the size instead
+
+   return tokens;
+}
+
+inline std::string join_strings(std::vector<std::string> arr, std::string sep, std::string wrap)
+{
+   std::string out = wrap + arr[0] + wrap;
+   for(unsigned int i = 1; i < arr.size(); i++) {
+      out += sep + wrap + arr[i] + wrap;
    }
+   return out;
+}
 
-   return rc;
+const string EMPTY_STRING;
+string to_sql_text_vec(string arg)
+{
+	if(!arg.empty())
+	{
+		vector<string> arg_split = split_by_delimiter(arg, string{','} );
+		if(!arg_split.empty())
+		{
+			//for (const auto& elem: arg_split) cout << elem << ' ';
+			//cout << '<' << endl;
+
+			string sql_text_arr{join_strings(arg_split,",","'")};
+			OUT_STREAM << "SQL ARRAY[" << sql_text_arr << "]"<< endl;
+			return sql_text_arr;
+		}
+	}
+	OUT_STREAM << "Cleared all groups for the given datasets." << endl;
+	return EMPTY_STRING;
+}
+
+int cmd_dbModGroups(int argc, char * argv[])
+{
+	string groups;
+	int sid_from, sid_to;
+	int rc;
+	switch(argc)
+	{
+		case 3:
+			groups   = std::string(argv[1]);
+			sid_from = std::stoi(argv[2]);
+			sid_to   = sid_from;
+			break;
+
+		case 4:
+			groups   = std::string(argv[1]);
+			sid_from = std::stoi(argv[2]);
+			sid_to   = std::stoi(argv[3]);
+			break;
+
+		default:
+			cout << endl
+				  << "Modifies list of groups which are allowed to access proprietary datasets given by SID." << endl
+				  << "SID (survey-id) is in the metadata file in the root of FITS-file store (typically /srv/datasets/<metadata>.csv)."
+				  << "Empty groups (\"\") arg clears the groups list in the database." << endl
+				  << "Usage:" << endl
+				  << "   dbmodgroups <groups> <SID-from> [SID-to]" << endl
+              << endl
+				  << "Examples (groups are comma separated):" << endl
+				  << "   vlkb-obscore datasets.conf dbmodgroups \"Group A,Group B\" 3 6" << endl
+				  << "   vlkb-obscore datasets.conf dbmodgroups \"\" 3 6" << endl;
+			rc = vlkb::EXIT_WITH_USAGE;
+			return rc;
+	}
+
+	try
+	{
+		string sql_groups_vec{to_sql_text_vec(groups)};
+
+		for(int i=sid_from; i<=sid_to; i++)
+			database::dbModifyGroups(i, sql_groups_vec,
+					vlkb::conf.getObsCorePublisher(), vlkb::conf.getDbUri(WITH_PASSWORD), vlkb::conf.getDbSchema());
+		rc = 0;
+	}
+	catch(exception& e)
+	{
+		rc = vlkb::EXIT_WITH_ERROR;
+		ERROR_STREAM << e.what() << endl;
+	}
+
+	return rc;
 }
 
 
 
 int cmd_dbRemove(int argc, char * argv[])
 {
-   int rc;
-   switch(argc)
-   {
-      case 2:
-         try
-         {
-            database::dbRemoveSurvey(stoi(argv[1]), vlkb::conf.getDbUri(WITH_PASSWORD), vlkb::conf.getDbSchema());
-            rc = 0;
-         }
-         catch(exception& e)
-         {
-            rc = vlkb::EXIT_WITH_ERROR;
-            ERROR_STREAM << e.what() << endl;
-         }
-         break;
-
-      default:
-         cout << "Usage: dbremove <SID>" << endl
-            << "removes survey metadata from database" << endl;
-         rc = vlkb::EXIT_WITH_USAGE;
-   }
-   return rc;
+	int rc;
+	switch(argc)
+	{
+		case 2:
+			try
+			{
+				database::dbRemoveSurvey(stoi(argv[1]), vlkb::conf.getDbUri(WITH_PASSWORD), vlkb::conf.getDbSchema());
+				rc = 0;
+			}
+			catch(exception& e)
+			{
+				rc = vlkb::EXIT_WITH_ERROR;
+				ERROR_STREAM << e.what() << endl;
+			}
+			break;
+
+		default:
+			cout << "Usage: dbremove <SID>" << endl
+				<< "removes survey metadata from database" << endl;
+			rc = vlkb::EXIT_WITH_USAGE;
+	}
+	return rc;
 }
 
 
@@ -383,59 +445,59 @@ int cmd_dbRemove(int argc, char * argv[])
 //-----------------------------------------------------------
 int main (int argc, char * argv[])
 {
-   const std::string progname = base_name(argv[0]);
-
-   if( argc < 3 )
-   {
-      vlkb::usage(progname);
-      return vlkb::EXIT_WITH_USAGE;
-   }
-
-   const string conf_filename(argv[1]);
-
-   // recognize command from string
-   const vlkb::cmd_set cmd(vlkb::to_cmd(argv[2]));
-
-   // sub-command arguments (skips: vlkb <conf-filename>)
-   int cmd_argc = argc - 2;
-   char ** cmd_argv = &(argv[2]);
-
-   vlkb::conf.read_config(conf_filename);
-/*
-   cout << "LOG DIR: " << vlkb::conf.getLogDir() << endl;
-   cout << "FITSDIR: " << vlkb::conf.getFitsDir() << endl;
-   cout << "DB[" << vlkb::conf.getDbSchema() << "]: " <<vlkb::conf.getDbUri() << endl;
-   cout << endl; 
-*/
-   LOG_open(vlkb::conf.getLogDir(), vlkb::conf.getLogFileName());
-   int rc;
-
-   try
-   {
-      switch(cmd)
-      {
-         case vlkb::dbinit:         rc = cmd_dbInit(cmd_argc,cmd_argv); break;
-         case vlkb::dbcheck:        rc = cmd_dbCheck(); break;
-         case vlkb::dblist:         rc = cmd_dbList(); break;
-         case vlkb::dbbounds:       rc = cmd_dbBounds(cmd_argc,cmd_argv); break;
-         case vlkb::dbfiles:        rc = cmd_dbFiles(cmd_argc,cmd_argv); break;
-         case vlkb::dbaddsurvey:    rc = cmd_dbAdd(cmd_argc,cmd_argv); break;
-         case vlkb::dbmodgroups:    rc = cmd_dbModGroups(cmd_argc,cmd_argv); break;
-         case vlkb::dbremovesurvey: rc = cmd_dbRemove(cmd_argc,cmd_argv); break;
-
-         default: assert(false);
-      }
-   }
-   catch(const invalid_argument& ex)
-   {
-      cout << ex.what() << endl;
-   }
-   catch(const exception& ex)
-   {
-      cerr <<  ex.what() << endl;
-   }
-
-   LOG_close();
-   return rc;
+	const std::string progname = base_name(argv[0]);
+
+	if( argc < 3 )
+	{
+		vlkb::usage(progname);
+		return vlkb::EXIT_WITH_USAGE;
+	}
+
+	const string conf_filename(argv[1]);
+
+	// recognize command from string
+	const vlkb::cmd_set cmd(vlkb::to_cmd(argv[2]));
+
+	// sub-command arguments (skips: vlkb <conf-filename>)
+	int cmd_argc = argc - 2;
+	char ** cmd_argv = &(argv[2]);
+
+	vlkb::conf.read_config(conf_filename);
+	/*
+		cout << "LOG DIR: " << vlkb::conf.getLogDir() << endl;
+		cout << "FITSDIR: " << vlkb::conf.getFitsDir() << endl;
+		cout << "DB[" << vlkb::conf.getDbSchema() << "]: " <<vlkb::conf.getDbUri() << endl;
+		cout << endl;
+	 */
+	LOG_open(vlkb::conf.getLogDir(), vlkb::conf.getLogFileName());
+	int rc;
+
+	try
+	{
+		switch(cmd)
+		{
+			case vlkb::dbinit:         rc = cmd_dbInit(cmd_argc,cmd_argv); break;
+			case vlkb::dbcheck:        rc = cmd_dbCheck(); break;
+			case vlkb::dblist:         rc = cmd_dbList(); break;
+			case vlkb::dbbounds:       rc = cmd_dbBounds(cmd_argc,cmd_argv); break;
+			case vlkb::dbfiles:        rc = cmd_dbFiles(cmd_argc,cmd_argv); break;
+			case vlkb::dbaddsurvey:    rc = cmd_dbAdd(cmd_argc,cmd_argv); break;
+			case vlkb::dbmodgroups:    rc = cmd_dbModGroups(cmd_argc,cmd_argv); break;
+			case vlkb::dbremovesurvey: rc = cmd_dbRemove(cmd_argc,cmd_argv); break;
+
+			default: assert(false);
+		}
+	}
+	catch(const invalid_argument& ex)
+	{
+		cout << ex.what() << endl;
+	}
+	catch(const exception& ex)
+	{
+		cerr <<  ex.what() << endl;
+	}
+
+	LOG_close();
+	return rc;
 }
 
-- 
GitLab