https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106732
Bug ID: 106732 Summary: lock cannot be generated in a special case Product: gcc Version: 12.1.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: benni.probst at gmx dot de Target Milestone: --- Created attachment 53500 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=53500&action=edit CLion bug analysis picture when using a recursive mutex and a scoped lock or unique lock or lock guard (does not matter) on the recursive mutex, code cannot be generated even if it worked in the function above. // // Created by benjamin-elias on 09.05.22. // #include "config_files.h" #include "global_custom_functions.h" #ifndef SCHOOL_PROJECT_DATABASE_CALL_OPS_H #define SCHOOL_PROJECT_DATABASE_CALL_OPS_H class database_call_ops { bool connection_set=false; std::recursive_mutex database_mutex{}; pqxx::connection *connection; protected: std::string addr, p, user, pass, datab; //TODO: set up connection and custom database call ops public: std::string schem, tab; protected: bool open=false; virtual std::string currentSchema() { const std::lock_guard lock(database_mutex); return schem; } virtual std::string currentTableName() { const std::lock_guard lock(database_mutex); return tab; } [[nodiscard]] bool isOpen(const std::string &table) const { const std::scoped_lock lock(database_mutex); return open and tab==table; } std::string currentDatabase() { return datab; } bool checkSchemaExists(const std::string &schema) { try{ std::string sql = "SELECT EXISTS(SELECT schema_name FROM information_schema.schemata WHERE schema_name = '"+schema+"');"; custom_function::replace_string_occurencies(sql," "," "); pqxx::nontransaction n(connection); pqxx::result r(n.exec(sql)); n.commit(); for (pqxx::result::const_iterator c = r.begin(); c != r.end();) { auto it = c; if(++c == r.end())[[likely]]{ return it[0].as<bool>();} else break; } } catch (std::exception &e){ std::cerr << "checkSchemaExists on " << schema << " failed for this reason: " << e.what(); } return false; } //returns if schema exists now bool createSchema(const std::string &schema) { if (checkSchemaExists(schema))[[likely]]return true; try{ std::string sql = "CREATE SCHEMA IF NOT EXISTS " + schema + " AUTHORIZATION " + user + " ; GRANT ALL ON SCHEMA " + schema + " TO " + datab + " ; GRANT ALL ON SCHEMA " + schema + " TO public;"; custom_function::replace_string_occurencies(sql," "," "); pqxx::work n(connection); n.exec(sql); n.commit(); } catch (std::exception &e){ std::cerr << "createSchema on " << schema << " failed for this reason: " << e.what(); exit(EXIT_FAILURE); } return checkSchemaExists(schema); } bool deleteSchema(const std::string &schema) { if (!checkSchemaExists(schema))[[unlikely]]{return false;} try{ std::string sql = "DROP SCHEMA IF EXISTS "+schema+" CASCADE;"; custom_function::replace_string_occurencies(sql," "," "); pqxx::work n(connection); n.exec(sql); n.commit(); } catch (std::exception &e){ std::cerr << "deleteSchema on " << schema << " failed for this reason: " << e.what(); } return !checkSchemaExists(schema); } bool checkTableExists(const std::string &schema, const std::string &table) { if (!checkSchemaExists(schema))[[unlikely]]{return false;} try{ std::string sql = "SELECT EXISTS (SELECT FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n" " ON n.oid = c.relnamespace WHERE n.nspname = '"+schema+"' AND c.relname = '"+table+ "' AND c.relkind = 'r' );"; custom_function::replace_string_occurencies(sql," "," "); pqxx::nontransaction n(connection); pqxx::result r(n.exec(sql)); n.commit(); for (pqxx::result::const_iterator c = r.begin(); c != r.end();) { auto it = c; if(++c == r.end())[[likely]]{return it[0].as<bool>();} else break; } } catch (std::exception &e){ std::cerr << "checkTableExists on (Schema: " << schema << ", Table: " << table << ") failed for this reason: " << e.what(); } return false; } std::vector<boost::tuple<std::string, std::string>> table_info(const std::string &schema, const std::string &table) { std::vector<boost::tuple<std::string,std::string>> out; try{ //table_name, column_name, data_type if(!checkTableExists(schema,table))[[unlikely]]{return out;} std::string sql = "SELECT column_name, data_type FROM information_schema.columns WHERE table_schema like '"+schema+"%' AND table_name like '"+table+"%' ;"; custom_function::replace_string_occurencies(sql," "," "); pqxx::nontransaction n(connection); pqxx::result r(n.exec(sql)); n.commit(); for (pqxx::result::const_iterator c = r.begin(); c != r.end();) { out.emplace_back(c[0].as<std::string>(),c[1].as<std::string>()); if(++c == r.end())[[unlikely]]{return out;} } } catch (std::exception &e){ std::cerr << "table_info on (Schema: " << schema << ", Table: " << table << ") failed for this reason: " << e.what(); return out; } return out; } bool deleteTable(const std::string &schema, const std::string &table) { if (!checkTableExists(schema,table))[[unlikely]]{return false;} try{ std::string sql = "DROP TABLE IF EXISTS "+schema+"."+table+";"; custom_function::replace_string_occurencies(sql," "," "); pqxx::work n(connection); n.exec(sql); n.commit(); } catch (std::exception &e){ std::cerr << "deleteTable on (Schema: " << schema << ", Table: " << table << ") failed for this reason: " << e.what(); } return !checkTableExists(schema,table); } private: /* * this is a variadic function to transform requested types into string literals that tell the SQL database * to generate tables with certain data types */ template<typename T1, typename T2, typename T3, typename... Args> std::string createTableRecursiveVariadicDefinition(T1 &&arg1, T2 &&arg2, T3 &&arg3, Args &&... args) { std::string out; if constexpr(String<T2>){ if (boost::ifind_first(std::forward<T2>(arg2), std::string("character varying")) or boost::ifind_first(std::forward<T2>(arg2), std::string("varchar")) or boost::ifind_first(std::forward<T2>(arg2), std::string("character")) or boost::ifind_first(std::forward<T2>(arg2), std::string("char")) or boost::ifind_first(std::forward<T2>(arg2), std::string("text")) or boost::ifind_first(std::forward<T2>(arg2), std::string("bytea")) or boost::ifind_first(std::forward<T2>(arg2), std::string("smallint")) or boost::ifind_first(std::forward<T2>(arg2), std::string("integer")) or boost::ifind_first(std::forward<T2>(arg2), std::string("bigint")) or boost::ifind_first(std::forward<T2>(arg2), std::string("decimal")) or boost::ifind_first(std::forward<T2>(arg2), std::string("numeric")) or boost::ifind_first(std::forward<T2>(arg2), std::string("real")) or boost::ifind_first(std::forward<T2>(arg2), std::string("double precision")) or boost::ifind_first(std::forward<T2>(arg2), std::string("smallserial")) or boost::ifind_first(std::forward<T2>(arg2), std::string("serial")) or boost::ifind_first(std::forward<T2>(arg2), std::string("bigserial")) or boost::ifind_first(std::forward<T2>(arg2), std::string("money")) or boost::ifind_first(std::forward<T2>(arg2), std::string("timestamp")) or boost::ifind_first(std::forward<T2>(arg2), std::string("TIMESTAMPTZ")) or boost::ifind_first(std::forward<T2>(arg2), std::string("date")) or boost::ifind_first(std::forward<T2>(arg2), std::string("a_var")) or boost::ifind_first(std::forward<T2>(arg2), std::string("interval")) or boost::ifind_first(std::forward<T2>(arg2), std::string("boolean")) or boost::ifind_first(std::forward<T2>(arg2), std::string("point")) or boost::ifind_first(std::forward<T2>(arg2), std::string("line")) or boost::ifind_first(std::forward<T2>(arg2), std::string("lseg")) or boost::ifind_first(std::forward<T2>(arg2), std::string("box")) or boost::ifind_first(std::forward<T2>(arg2), std::string("path")) or boost::ifind_first(std::forward<T2>(arg2), std::string("polygon")) or boost::ifind_first(std::forward<T2>(arg2), std::string("circle")) or boost::ifind_first(std::forward<T2>(arg2), std::string("cidr")) or boost::ifind_first(std::forward<T2>(arg2), std::string("inet")) or boost::ifind_first(std::forward<T2>(arg2), std::string("macaddr")) or boost::ifind_first(std::forward<T2>(arg2), std::string("tsvector")) or boost::ifind_first(std::forward<T2>(arg2), std::string("tsquery")) or boost::ifind_first(std::forward<T2>(arg2), std::string("any")) or boost::ifind_first(std::forward<T2>(arg2), std::string("anyelement")) or boost::ifind_first(std::forward<T2>(arg2), std::string("anyarray")) or boost::ifind_first(std::forward<T2>(arg2), std::string("anynonarray")) or boost::ifind_first(std::forward<T2>(arg2), std::string("anyenum")) or boost::ifind_first(std::forward<T2>(arg2), std::string("anyrange")) or boost::ifind_first(std::forward<T2>(arg2), std::string("cstring")) or boost::ifind_first(std::forward<T2>(arg2), std::string("internal")) or boost::ifind_first(std::forward<T2>(arg2), std::string("language_handler")) or boost::ifind_first(std::forward<T2>(arg2), std::string("fdw_handler")) or boost::ifind_first(std::forward<T2>(arg2), std::string("record")) or boost::ifind_first(std::forward<T2>(arg2), std::string("trigger")) or boost::ifind_first(std::forward<T2>(arg2), std::string("void")) ){ out+=(std::string) std::forward<T1>(arg1)+" "+(std::string) std::forward<T2>(arg2)+" "+(std::string) std::forward<T3>(arg3); } else{ if constexpr(sizeof...(args)==0){ out+=(std::string) std::forward<T1>(arg1)+" "+(std::string) std::forward<T2>(arg2)+" "+(std::string) std::forward<T3>(arg3); } else{ out+=(std::string) std::forward<T1>(arg1)+" text "+(std::string) std::forward<T3>(arg3); } } } else{ if constexpr(std::is_same_v<typename std::decay<T2>::type,unsigned char> or std::is_same_v<typename std::decay<T2>::type,unsigned short> or std::is_same_v<typename std::decay<T2>::type,char> or std::is_same_v<typename std::decay<T2>::type,short> ){ out+=(std::string) std::forward<T1>(arg1)+" smallint "+(std::string) std::forward<T3>(arg3); } if constexpr(std::is_same_v<typename std::decay<T2>::type,unsigned int> or std::is_same_v<typename std::decay<T2>::type,int> ){ out+=(std::string) std::forward<T1>(arg1)+" integer "+(std::string) std::forward<T3>(arg3); } if constexpr(std::is_same_v<typename std::decay<T2>::type,unsigned long int> or std::is_same_v<typename std::decay<T2>::type,unsigned long long int> or std::is_same_v<typename std::decay<T2>::type,long int> or std::is_same_v<typename std::decay<T2>::type,long long int> ){ out+=(std::string) std::forward<T1>(arg1)+" bigint "+(std::string) std::forward<T3>(arg3); } if constexpr(std::is_same_v<typename std::decay<T2>::type,float>){ out+=(std::string) std::forward<T1>(arg1)+" real "+(std::string) std::forward<T3>(arg3); } if constexpr(std::is_same_v<typename std::decay<T2>::type,double> or std::is_same_v<typename std::decay<T2>::type,long double> ){ out+=(std::string) std::forward<T1>(arg1)+" double precision "+(std::string) std::forward<T3>(arg3); } if constexpr(std::is_same_v<typename std::decay<T2>::type,bool>){ out+=(std::string) std::forward<T1>(arg1)+" boolean "+(std::string) std::forward<T3>(arg3); } } if constexpr(sizeof...(args)==0){ return out; } else{ return out+", "+createTableRecursiveVariadicDefinition(args...); } } public: bool connect(const std::string &database) { if(connection.is_open())[[likely]]{ std::cout << "Database " << database << " was already open."; return true; } INFO << "Connecting on Database " << database; for (char i = 0; i < 3; i++) { try { std::string out = "user=" + user + " password=" + pass + " host=" + addr + " port=" + p + " dbname=postgres" + " target_settion_attrs=read-write"; connection = pqxx::connection(out); if (connection.is_open())[[likely]]{ std::cout << "Opened database successfully: " << this->datab; datab = database; return true; } else { CUSTOM_THROW(1,"Can't open database") } } catch (const std::exception &e) { std::cerr << "connect on Database " << database << " failed for this reason: " << e.what(); std::system("service postgresql restart"); std::cout << "Restart of database done!"; } } if(!connection.is_open())[[likely]]{ std::cerr << "Database " << database << " could finally not be connected to!"; exit(EXIT_FAILURE); } return false; } void disconnect() { connection.close(); } //this function creates a table and will also create the schema if it does not exist template<typename... Args> bool createTable(const std::string &schema, const std::string &table, const std::string &pre_options, const std::string &options, Args &&... args) { if(!checkSchemaExists(schema))[[unlikely]]{ if(!createSchema(schema))[[unlikely]]{ std::cerr << "The following schema could not be created: " << schema; exit(EXIT_FAILURE); } } if(checkTableExists(schema,table))[[likely]]{return false;} std::string sql = "CREATE "+pre_options+" TABLE "+options+" "+schema+"."+table+" ( "+createTableRecursiveVariadicDefinition(args...)+" ) ;"; while(boost::algorithm::contains(sql," ")){ custom_function::replace_string_occurencies(sql," "," "); } try{ pqxx::work n(connection); n.exec(sql); n.commit(); } catch (std::exception &e){ std::cerr << "createTable on (SQL command: " << sql << ") failed for this reason: " << e.what(); exit(EXIT_FAILURE); } return checkTableExists(schema,table); } /* * automatically open table and create scheme or table if it does not exists */ template<typename... Args> bool openTable(const std::string &database, const std::string &schema, const std::string &table,Args &&...args) { if(!connect(database))[[unlikely]]{ std::cerr << "Could not connect to database and clName: " << database << " at " << schema << "." << table << " !!!"; std::exit(EXIT_FAILURE); } schem=schema; tab=table; if (checkTableExists(schema,table))[[likely]]{ open = true; return open; } else{ open=this->createTable(schema,table,args...); if (!open){ std::cerr << "Cannot create tabel for open request (database: "<< database << ", schema: " << schema << ", table: "<< table << ")"; return open; } return open; } } //check if the block can be reader bool check_block_exists(const std::string& identifier) { std::string sql = "SELECT identifier FROM " + schem + "." + tab + " WHERE identifier = decodeInit('"+ identifier + "', 'hex');"; custom_function::replace_string_occurencies(sql," "," "); pqxx::nontransaction nf(connection); pqxx::result rf(nf.exec(sql)); nf.commit(); if (rf.empty()) { return false; } return rf[0][0].as<std::string>()==identifier; } //get the value of the sha id, so basically retrieve/return block std::string read_block(const std::string& identifier) { if(!check_block_exists(identifier))[[unlikely]]{ std::cerr << "The block "+identifier+"was not found on the database." << std::endl; std::exit(EXIT_FAILURE); } std::string sql = "SELECT identifier,block FROM " + schem + "." + tab + " WHERE identifier = decodeInit('" + identifier + "', 'hex');"; custom_function::replace_string_occurencies(sql," "," "); pqxx::nontransaction nf(connection); pqxx::result rf(nf.exec(sql)); nf.commit(); if(rf.size()>1){ std::cerr << "The block hash "+identifier+"was found multiple times on the database." << std::endl; } return rf[0][1].as<std::string>(); } //write block by setting a string to the sha_id bool write_block(const std::string& identifier, const std::string& block) { if(check_block_exists(identifier))[[unlikely]]{ std::cerr << "The block "+identifier+"was found on the database and will be updated." << std::endl; } std::string sql = "INSERT INTO " + this->schem + "." + this->tab + " (identifier,block) VALUES (decodeInit('" + identifier +"', 'hex'), decodeInit('" + block +"', 'hex')) ON CONFLICT (identifier) DO UPDATE SET block = excluded.block ;"; custom_function::replace_string_occurencies(sql," "," "); pqxx::work n2(connection); n2.exec(sql); n2.commit(); return true; } //auto create block table explicit database_call_ops(const std::string &db_config_file) {//: postgresSQL(db_config_file) { try{ std::filesystem::path config(db_config_file); config_files configuration = config_files(config); unsigned char count=0; for(auto &r:configuration.read()){ switch(count){ case 0:addr=r;break; case 1:p=r;break; case 2:user=r;break; case 3:pass=r;break; case 4:datab=r;break; case 5:schem=r;break; default:tab=r;break; } count++; } std::cout << "Successfully reader Database configuration file at postgreSQL \'" << config.c_str() << "\' module." ; } catch (std::exception &e){ try{ std::fstream file; file.exceptions(std::ofstream::failbit | std::ofstream::badbit); file.open(db_config_file, std::ios_base::binary); (void) file.write(std::string().c_str(), 0); file.close(); std::cerr<<"Could not configure Database, because there was a file error (check please) at postgreSQL \'config_database\' (address: "+addr+", port: "+p+", user: "+user+", password: "+pass+") error: " << e.what(); std::cout<<"Created a new database config file at the spot you chose. Hopefully this was not by accident so you should not forget to delete the file at the wrong spot."; } catch(std::exception& e){ std::cerr<<"Could not configure Database, because there was a file error (check please) at postgreSQL \'config_database\' (address: "+addr+", port: "+p+", user: "+user+", password: "+pass+") error: " << e.what(); std::exit(EXIT_FAILURE); } } if (!this->openTable(datab, schem, tab, "", "IF NOT EXISTS", "id", (unsigned long long int) 0, "GENERATED ALWAYS AS IDENTITY", "identifier", "bytea", "UNIQUE NOT NULL", "block", "bytea", "NOT NULL", "", "", "PRIMARY KEY(id)" )) { std::cerr << "Database table could not be created or opened!"; std::exit(EXIT_FAILURE); } } }; #endif //SCHOOL_PROJECT_DATABASE_CALL_OPS_H