#include "sqlgen.h" #include "../stringtools.h" #include #include enum CPPFileTokenType { CPPFileTokenType_Code, CPPFileTokenType_Comment }; struct CPPToken { CPPToken(const std::string& data, CPPFileTokenType type) : data(data), type(type) { } std::string data; CPPFileTokenType type; }; enum TokenizeState { TokenizeState_None, TokenizeState_CommentMultiline, TokenizeState_CommentSingleline }; std::vector tokenizeFileRegex(std::string &cppfile) { //So much for that. Causes stack overflows std::regex find_comments("(/\\*(\\S|\\s)*?\\*/)|(//.*)", std::regex::ECMAScript); auto comments_begin=std::regex_iterator(cppfile.begin(), cppfile.end(), find_comments); auto comments_end=std::regex_iterator(); std::vector tokens; size_t lastPos=0; for(auto i=comments_begin;i!=comments_end;++i) { auto m=*i; size_t pos=m.position(0); if(lastPos tokenizeFile(std::string &cppfile) { std::string cdata; std::vector tokens; int state = 0; for (size_t i = 0; i < cppfile.size(); ++i) { char ch = cppfile[i]; cdata += ch; switch (state) { case 0: if (ch == '/') { state = 1; } break; case 1: if (ch == '*') { state = 2; if (cdata.size() > 2) { tokens.push_back(CPPToken(cdata.substr(0, cdata.size() - 2), CPPFileTokenType_Code)); } cdata = "/*"; } else if (ch == '/') { state = 3; if (cdata.size() > 2) { tokens.push_back(CPPToken(cdata.substr(0, cdata.size() - 2), CPPFileTokenType_Code)); } cdata = "//"; } else { state = 0; } break; case 2: if (ch == '*') { state = 4; } break; case 3: if (ch == '\n') { state = 0; if (cdata.size() > 2 && cdata[cdata.size() - 2] == '\r') { tokens.push_back(CPPToken(cdata.substr(0, cdata.size() - 2), CPPFileTokenType_Comment)); cdata = "\r\n"; } else { tokens.push_back(CPPToken(cdata.substr(0, cdata.size() - 1), CPPFileTokenType_Comment)); cdata = "\n"; } cdata = "\n"; } break; case 4: if (ch == '/') { state = 0; tokens.push_back(CPPToken(cdata, CPPFileTokenType_Comment)); cdata.clear(); } else if(ch!='*') { state = 2; } break; } } if (!cdata.empty()) { tokens.push_back(CPPToken(cdata, CPPFileTokenType_Code)); } return tokens; } struct AnnotatedCode { AnnotatedCode(std::map annotations, std::string code) : annotations(annotations), code(code) { } AnnotatedCode(std::string code) : code(code) { } std::map annotations; std::string code; }; std::string cleanup_annotation(const std::string& annotation) { int state=0; std::string ret; for(size_t i=0;i0) { was_in_function=true; } if(c==0 && was_in_function) { return data.substr(0, i+1); } } return std::string(); } std::map parseAnnotations(const std::string& data) { int state = 0; std::string name; std::string content; std::map ret; for (size_t i = 0; i < data.size(); ++i) { char ch = data[i]; switch (state) { case 0: { if (ch == '@') state = 1; } break; case 1: { if (ch=='\r' || ch == '\n' || ch==' ') { name = trim(name); state = 2; content += ch; } else { name += ch; } }break; case 3: case 2: { if (ch == '@') { state = 1; ret[name] = trim(cleanup_annotation(content)); name.clear(); content.clear(); } else if (ch == '*' && state==2) { content += ch; state = 3; } else if (ch == '/' && state == 3) { state = 0; ret[name] = trim(cleanup_annotation(content)); name.clear(); content.clear(); } else { content += ch; state = 2; } }break; } } return ret; } std::map parseAnnotationSingle(const std::string& data) { //Doesn't work with MSVC2015 (out of stack memory): "@([^ \\r\\n]*)[ ]*(((?!@)(?!\\*/)(\\S|\\s))*)" //std::regex find_annotations = std::regex("@([^ \\r\\n]*)[ ]*((\\S|\\s)*?)(?=(\\*/)|@)", std::regex::ECMAScript); std::regex find_annotations = std::regex("@([^ \\r\\n]*)[ ]*((\\S|\\s)*?)", std::regex::ECMAScript); std::map ret; for (auto it = std::regex_iterator(data.begin(), data.end(), find_annotations); it != std::regex_iterator(); ++it) { auto m = *it; std::string annotation_text = m[2].str(); ret[m[1].str()] = trim(cleanup_annotation(annotation_text)); } return ret; } std::vector getAnnotatedCode(const std::vector& tokens) { std::vector ret; for(size_t i=0;i annotations = parseAnnotations(tokens[i].data); if (tokens[i].data.find("//") == 0) { annotations = parseAnnotationSingle(tokens[i].data); } ret.push_back(AnnotatedCode(tokens[i].data)); if(!annotations.empty()) { if(i+1 parseReturnTypes(std::string return_str) { std::vector toks; Tokenize(return_str, toks, ","); std::vector ret; for(size_t i=0;i& types) { std::regex find_var(":([^ (]*)\\(([^)]*?)\\)",std::regex::ECMAScript); size_t lastPos=0; std::string retSql; for(auto it=std::regex_iterator(sql.begin(), sql.end(), find_var); it!=std::regex_iterator();++it) { auto m=*it; if(m.position()>lastPos) { retSql+=sql.substr(lastPos, m.position()-lastPos); lastPos=m.position()+m[0].length(); } retSql+="?"; types.push_back(ReturnType(m[2].str(), m[1].str())); } if(lastPos structures; std::string variables; }; void generateStructure(std::string name, std::vector return_types, GeneratedData& gen_data, bool use_exists) { if(gen_data.structures.find(name)!=gen_data.structures.end() && (!use_exists || gen_data.structures[name].use_exist ) ) { return; } std::string code; code+="\tstruct "+name+"\r\n"; code+="\t{\r\n"; if(use_exists) { code+="\t\tbool exists;\r\n"; } for(size_t i=0;i", return_type); if(struct_name=="string") { return_type="std::vector"; } if(struct_name=="blob") { return_type="std::vector"; } return_vector=true; } StatementType stmt_type=StatementType_None; size_t op_pos; if( (op_pos=strlower(sql).find("select"))!=std::string::npos) { stmt_type=StatementType_Select; } size_t new_pos; if( (new_pos = strlower(sql).find("delete"))!=std::string::npos && new_pos return_types=parseReturnTypes(return_vals); bool use_struct=false; bool use_cond=false; bool use_exists=false; bool use_raw=false; if(return_types.size()>1) { use_struct=true; } if(return_vector) { if(return_types.size()>1) { generateStructure(struct_name, return_types, gen_data, false); } } else if(strlower(return_type)!="void" && strlower(return_type)!="bool") { if(return_types.size()==1) { if(return_types[0].type.find("_raw")!=std::string::npos) { return_types[0].type=greplace("_raw", "", return_types[0].type); use_raw=true; struct_name=return_types[0].type; } else { use_cond=true; use_exists=true; struct_name=generateConditional(return_types[0], gen_data); } } else { generateStructure(struct_name, return_types, gen_data, true); use_exists=true; } } std::vector params; std::string parsedSql=parseSqlString(sql, params); if(check) { IQuery *q=db->Prepare("EXPLAIN "+parsedSql, true); if(q==NULL) { std::cout << "ERROR preparing statement: " << parsedSql << " Function: " << func << std::endl; return AnnotatedCode(input.annotations, ""); } else { q->Read(); } if (stmt_type == StatementType_Select) { size_t select_pos = strlower(parsedSql).find("select"); size_t from_pos = strlower(parsedSql).find("from"); std::string select_vars = trim(parsedSql.substr(select_pos + 6, from_pos - select_pos - 6)); if (!select_vars.empty() && select_vars != "*") { std::vector return_exp_vars; TokenizeMail(select_vars, return_exp_vars, ","); for (size_t i = 0; i < return_exp_vars.size(); ++i) { return_exp_vars[i] = trim(return_exp_vars[i]); size_t as_pos = strlower(return_exp_vars[i]).find(" as "); if (as_pos != std::string::npos) { return_exp_vars[i] = trim(return_exp_vars[i].substr(as_pos + 4)); } else if (return_exp_vars[i].find(".") != std::string::npos) { return_exp_vars[i] = getafter(".", return_exp_vars[i]); } } for (size_t i = 0; i < return_types.size(); ++i) { if (std::find(return_exp_vars.begin(), return_exp_vars.end(), return_types[i].name) == return_exp_vars.end()) { std::cout << "ERROR Cannot find variable '" << return_types[i].name << "' in SQL: " << parsedSql << " Function: " << func << std::endl; return AnnotatedCode(input.annotations, ""); } } } } } std::string return_outer=return_type; if(return_vector) { return_outer="std::vector<"; if(use_struct) { return_outer+=(classname.empty()?"":classname+"::")+struct_name; } else { if(return_types.empty()) { std::cout << "@return is missing!" << std::endl; } else { if(return_types[0].type=="string") { return_outer+="std::string"; } else if(return_types[0].type=="blob") { return_outer+="std::string"; } else { return_outer+=return_types[0].type; } } } return_outer+=">"; } else if(use_cond) { return_outer=(classname.empty()?"":classname+"::")+struct_name; return_type=struct_name; } else if(struct_name!="string" && struct_name!="void" && struct_name!="int" && struct_name!="bool" && struct_name!="int64") { return_outer=(classname.empty()?"":classname+"::")+struct_name; return_type=struct_name; } if(return_outer=="string") return_outer="std::string"; if(return_type=="string") return_type="std::string"; if(return_type=="string") return_type="std::wstring"; std::string funcdecl=return_type+" "+func_s_name+"("; std::string code="\r\n"+return_outer+" "+funcsig+"("; for(size_t i=0;i0) { code+=", "; funcdecl+=", "; } std::string type=params[i].type; if(type=="string" || type=="std::string" ) { type="const std::string&"; } else if(type=="blob") { type="const std::string&"; } code+=type+" "+params[i].name; funcdecl+=type+" "+params[i].name; } if(params.empty()) { code+="void"; funcdecl+="void"; } code+=")\r\n{\r\n"; funcdecl+=");"; gen_data.funcdecls+="\t"+funcdecl+"\r\n"; gen_data.variables+="\tIQuery* "+query_name+";\r\n"; gen_data.createQueriesCode+="\t"+query_name+"=NULL;\r\n"; gen_data.destroyQueriesCode+="\tdb->destroyQuery("+query_name+");\r\n"; code+="\tif("+query_name+"==NULL)\r\n\t{\r\n\t"; code+="\t"+query_name+"=db->Prepare(\""+parsedSql+"\", false);\r\n"; code+="\t}\r\n"; for(size_t i=0;iBind("+params[i].name+".c_str(), (_u32)"+params[i].name+".size());\r\n"; } else { code+="\t"+query_name+"->Bind("+params[i].name+");\r\n"; } } bool has_return=false; if(stmt_type==StatementType_Select) { code+="\tdb_results res="+query_name+"->Read();\r\n"; } else if(stmt_type==StatementType_Delete || stmt_type==StatementType_Insert || stmt_type==StatementType_Update || stmt_type==StatementType_Create || stmt_type==StatementType_Drop ) { if(return_type=="bool") { code+="\tbool ret = "+query_name+"->Write();\r\n"; has_return=true; } else { code+="\t"+query_name+"->Write();\r\n"; } } if(!params.empty()) { code+="\t"+query_name+"->Reset();\r\n"; } if(has_return) { code+="\treturn ret;\r\n"; } if(return_vector) { code+="\tstd::vector<"; if(use_struct) { code+=(classname.empty()?"":classname+"::")+struct_name; } else { if(!return_types.empty()) { if(return_types[0].type=="string") { code+="std::string"; } else if(return_types[0].type=="blob") { code+="std::string"; } else { code+=return_types[0].type; } } else { std::cout << "@return is missing!" << std::endl; //TODO error handling } } code+="> ret;\r\n"; code+="\tret.resize(res.size());\r\n"; code+="\tfor(size_t i=0;i& annotated_code) { for(size_t i=0;i::iterator sql_it=curr.annotations.find("sql"); if(sql_it!=curr.annotations.end()) { db->Write(sql_it->second); } } } } } void generateCode1(IDatabase* db, std::vector& annotated_code, GeneratedData& generated_data) { for(size_t i=0;i0) { if(!new_content.empty()) ret+="{"; ret+=new_content; new_content.clear(); } else { ret+=data[i]; } } return ret; } std::string generateSetupFunction(std::string data, GeneratedData& generated_data) { return replaceFunctionContent("\r\n"+generated_data.createQueriesCode, data); } std::string generateDestructionFunction(std::string data, GeneratedData& generated_data) { return replaceFunctionContent("\r\n"+generated_data.destroyQueriesCode, data); } void generateCode2(std::vector& annotated_code, GeneratedData& generated_data) { for(size_t i=0;i& annotated_code) { std::string code; for(size_t i=0;isecond.code; } return code; } std::string placeData(std::string headerfile, GeneratedData generated_data) { std::string t_headerfile=setbetween("//@-SQLGenFunctionsBegin", "//@-SQLGenFunctionsEnd", getStructureCode(generated_data)+"\r\n\r\n"+generated_data.funcdecls+"\t", headerfile); if(t_headerfile.empty()) { std::cout << "ERROR: Cannot find \"//@-SQLGenFunctionsBegin\" or \"//@-SQLGenFunctionsEnd\" in Header-file" << std::endl; return headerfile; } t_headerfile=setbetween("//@-SQLGenVariablesBegin", "//@-SQLGenVariablesEnd", generated_data.variables+"\t", t_headerfile); if(t_headerfile.empty()) { std::cout << "ERROR: Cannot find \"//@-SQLGenVariablesBegin\" or \"//@-SQLGenVariablesEnd\" in Header-file" << std::endl; return headerfile; } return t_headerfile; } void sqlgen(IDatabase* db, std::string &cppfile, std::string &headerfile) { std::vector tokens=tokenizeFile(cppfile); std::vector annotated_code=getAnnotatedCode(tokens); GeneratedData generated_data; setup1(db, annotated_code); generateCode1(db, annotated_code, generated_data); generateCode2(annotated_code, generated_data); cppfile=getCode(annotated_code); headerfile=placeData(headerfile, generated_data); }