#pragma implementation #include #include #include #include #include #include #include #include #include #include "/usr/include/mysql/mysql.h" #include "/usr/include/mysql/errmsg.h" #include "tlmpsql.h" #include #include "tlmpsql.m" #include #include using namespace std; static unsigned int default_tcpport=0; void nsql_settcpport (unsigned int port) { default_tcpport = port; } static string default_charset("utf8mb4"); static TLMP_OPTION charser_opt ("default_charset",P_MSG_U(O_DEFAULT_CHARSET,"Default character set for SQL connections"),default_charset); struct NSQL_INTERNAL{ MYSQL my; bool connected; char *server; char *db; char *user; char *passwd; unsigned int port; char *socketpath; bool errormode; // Display error message or not SSTREAM *sout; // Optional stream for SQL action only string charset; NSQL_INTERNAL() :charset(default_charset) { errormode = true; server = NULL; db = NULL; user = NULL; passwd = NULL; connected = false; sout = NULL; port = default_tcpport; socketpath = NULL; } ~NSQL_INTERNAL() { free(server); free(db); free(user); free(passwd); free(socketpath); } }; PUBLIC NSQL::NSQL( const char *server, const char *db) { internal = new NSQL_INTERNAL; internal->server = strdup(server); internal->db = strdup(db); } PUBLIC NSQL::NSQL( const char *server, const char *db, const char *user, const char *passwd) { internal = new NSQL_INTERNAL; internal->server = strdup(server); internal->db = strdup(db); if (user != NULL && user[0] != '\0'){ internal->user = strdup(user); if (passwd != NULL && passwd[0] != '\0'){ internal->passwd = strdup(passwd); } } } static char *strdup_if (const char *pt) { char *ret = nullptr; if (pt != nullptr) ret = strdup(pt); return ret; } PUBLIC NSQL::NSQL(const NSQL &n) { internal = new NSQL_INTERNAL; internal->server = strdup_if(n.internal->server); internal->db = strdup_if(n.internal->db); internal->db = strdup_if(n.internal->user); internal->db = strdup_if(n.internal->passwd); } PUBLIC bool NSQL::showerrormode (bool mode) { bool old = internal->errormode; internal->errormode = mode; return old; } /* Record the unix domain path to use to talk to the localhost mysql server */ PUBLIC void NSQL::setunixpath (const char *path) { free (internal->socketpath); internal->socketpath = strdup(path); } /* Record the TCP port used to reach the MySQL server */ PUBLIC void NSQL::settcpport (const char *port) { struct servent *s = getservbyname(port,"tcp"); if (s == NULL){ tlmp_error (MSG_U(E_IVLDTCPPORT,"Invalid TCP port %s\n"),port); }else{ internal->port = ntohs(s->s_port); } } PUBLIC void NSQL::settcpport (unsigned int port) { internal->port = port; } PUBLIC NSQL::NSQL( SSTREAM *sout) { internal = new NSQL_INTERNAL; internal->sout = sout; internal->connected = true; } PUBLIC NSQL::NSQL(NSQL_ARGS &args) { internal = new NSQL_INTERNAL; internal->server = strdup(args.server); internal->db = strdup(args.db); } PUBLIC NSQL::~NSQL() { disconnect(); delete internal; } PUBLIC int NSQL::disconnect() { int ret = 0; if (internal->connected && internal->sout == NULL){ mysql_close (&internal->my); internal->connected = false; } return ret; } // This will force a reconnection without doing mysql_close of the current connection. // This is usually used when doing a fork() so the child will setup a new connection // instead of using the parent connection (and causing confusion). PUBLIC int NSQL::forget_connection() { int ret = 0; if (internal->connected && internal->sout == NULL){ internal->connected = false; } return ret; } void NSQL::set_default_charset(PARAM_STRING def) { default_charset = def.ptr; } PUBLIC void NSQL::set_charset(PARAM_STRING cset) { internal->charset = cset.ptr; if (internal->connected){ mysql_set_character_set (&internal->my,cset.ptr); } } PUBLIC int NSQL::connect() { if (!internal->connected){ mysql_init (&internal->my); if (mysql_real_connect(&internal->my ,internal->server,internal->user,internal->passwd ,internal->db,internal->port,internal->socketpath,0)){ internal->connected = true; mysql_set_character_set (&internal->my,internal->charset.c_str()); } } return internal->connected ? 0 : -1; } PUBLIC int NSQL::query (const char *req) { int ret = -1; if (internal->sout != NULL){ tlmp_error (MSG_U(E_SQLOUTONLY,"NSQL: Only SQL action on this handle\n\t%s\n"),req); }else if (connect() != -1){ for (int i=0; i<2; i++){ ret = mysql_query (&internal->my,req) ? -1 : 0; if (ret == -1){ bool server_gone = mysql_errno(&internal->my) == CR_SERVER_GONE_ERROR; if (i==0 && server_gone){ disconnect(); if (connect()==-1){ tlmp_error ("%s\n",MSG_U(E_RECONFAIL,"NSQL: Server gone, reconnect failed")); break; }else{ tlmp_warning ("%s\n",MSG_U(E_RECONNOW,"NSQL: Server gone, reconnect ok")); } }else{ if (internal->errormode){ tlmp_error (MSG_U(E_QUERY,"NSQL query error: %s\n(%s)\n") ,req,error()); } if (server_gone){ tlmp_error ("%s\n",MSG_U(E_WILLRECON,"NSQL: will try to reconnect at next query")); disconnect(); } } }else{ break; } } }else{ tlmp_error (MSG_U(E_MYSQLCON,"Connection failed to MySql: %s\n"),error()); } return ret; } static int nsql_vsnprintf (string &buf, const char *ctl, va_list list) { int ret = 0; buf.reserve(10000); const char *startctl = ctl; const char *ptquote = NULL; while (*ctl != '\0'){ char car = *ctl++; if (car == '\''){ ptquote = ctl; // We remember that so we can overwrite '%s' with NULL later buf += car; }else if (car != '%'){ buf += car; }else if (*ctl == '%'){ buf += '%'; ctl++; }else{ bool longopt = false; bool longlongopt = false; bool minus = false; int frm1 = -1; int frm2 = -1; if (*ctl == '-'){ minus = true; ctl++; } if (isdigit(*ctl)){ frm1 = atoi(ctl); ctl = str_skipdig(ctl); } if (*ctl == '.'){ ctl++; if (isdigit(*ctl)){ frm2 = atoi(ctl); ctl = str_skipdig(ctl); } } car = *ctl++; if (car == 'l'){ longopt = true; car = *ctl++; }else if (car == 'L'){ longlongopt=true; car = *ctl++; } if (car == 's'){ const char *val = va_arg(list,char*); if (val == NULL){ if (ptquote != NULL && ptquote[0] == '%' && ptquote[1] == 's' && ptquote[2] == '\''){ // Remove the ' we put earlier buf.resize(buf.size()-1); buf += "NULL"; ctl++; }else{ buf += "NULL"; } }else{ while (*val != '\0'){ char valc = *val++; if (valc == '\\'){ buf += '\\'; }else if (valc == '\''){ buf += '\\'; }else if (valc == '"'){ buf += '\\'; } buf += valc; } } }else if (car == 'u'){ if (!minus && frm1 == -1 && frm2 == -1){ char tmp[30]; if (longopt){ unsigned long val = va_arg(list,unsigned long); snprintf (tmp,sizeof(tmp),"%lu",val); }else if (longlongopt){ unsigned long long val = va_arg(list,unsigned long long); snprintf (tmp,sizeof(tmp),"%Lu",val); }else{ unsigned val = va_arg(list,unsigned); snprintf (tmp,sizeof(tmp),"%u",val); } buf += tmp; } }else if (car == 'd'){ if (!minus && frm1 == -1 && frm2 == -1){ char tmp[30]; if (longopt){ long val = va_arg(list,long); snprintf (tmp,sizeof(tmp),"%ld",val); }else if (longlongopt){ long long val = va_arg(list,long long); snprintf (tmp,sizeof(tmp),"%Ld",val); }else{ int val = va_arg(list,int); snprintf (tmp,sizeof(tmp),"%d",val); } buf += tmp; } }else if (car == 'c'){ if (!minus && frm1 == -1 && frm2 == -1){ char val = va_arg(list,int); buf += val; } }else{ tlmp_error (MSG_U(E_SPRINTF,"Invalid format character %c: %s\n"),car,startctl); //fprintf (stderr,"Invalid format character %c: %s\n",car,startctl); ret = -1; break; } } } #if 0 FILE *fout = fopen ("/tmp/sql.out","a"); if (fout != NULL){ fputs (buf.c_str(),fout); fputs ("\n",fout); fclose (fout); } #endif return ret == -1 ? ret : (int)buf.size(); } PUBLIC int NSQL::queryf (const char *ctl, ...) { va_list list; va_start (list,ctl); int ret = vqueryf (ctl,list); va_end (list); return ret; } PUBLIC int NSQL::vqueryf (const char *ctl, va_list list) { string buf; nsql_vsnprintf (buf,ctl,list); return query (buf.c_str()); } /* Action query with no useful result set */ PUBLIC int NSQL::a_query (const char *req) { int ret = -1; if (internal->sout != NULL){ internal->sout->printf ("%s;\n",req); ret = 0; }else{ ret = query (req); if (ret != -1){ MYSQL_RES *res = store_result(); free_result(res); ret = mysql_affected_rows(&internal->my); } } return ret; } PUBLIC int NSQL::a_queryf (const char *ctl, ...) { va_list list; va_start (list,ctl); int ret = a_vqueryf (ctl,list); va_end (list); return ret; } PUBLIC int NSQL::a_vqueryf (const char *ctl, va_list list) { string buf; nsql_vsnprintf (buf,ctl,list); return a_query (buf.c_str()); } PUBLIC const char * NSQL::error () { return mysql_error(&internal->my); } PUBLIC int NSQL::geterrno() { return mysql_errno(&internal->my); } PUBLIC MYSQL_RES *NSQL::store_result() { return mysql_store_result(&internal->my); } PUBLIC void NSQL::free_result(MYSQL_RES *res) { if (res != NULL) mysql_free_result(res); } PUBLIC int NSQL::getlastid() { int ret = -1; if (internal->connected){ ret = mysql_insert_id(&internal->my); } return ret; } PUBLIC NSQL_ARGS::NSQL_ARGS() { server = db = NULL; } PUBLIC NSQL_ARGS::NSQL_ARGS(const char *_server, const char *_db) { server = db = NULL; setserver (_server); setdb (_db); } PUBLIC NSQL_ARGS::~NSQL_ARGS() { free (server); free (db); } PUBLIC void NSQL_ARGS::setserver( const char *s) { free (server); server = NULL; if (s != NULL) server = strdup(s); } PUBLIC void NSQL_ARGS::setdb ( const char *s) { free (db); db = NULL; if (s != NULL) db = strdup(s); } PUBLIC int NSQL_ARGS::isok() { return server != NULL && db != NULL; } int nsql_parseargs(int argc, char *argv[], NSQL_ARGS &ar) { int i; for (i=1; iadd (ss); ret = tmp; } return ret; } PUBLIC const char *NSQL_ENCODE::enc(const std::string &s) { return enc (s.c_str()); } PUBLIC const char *NSQL_ENCODE::enc(const SSTRING &s) { return enc (s.c_str()); } PUBLIC const char *NSQL_ENCODE::enc(const SSTRING *s) { return enc (s->c_str()); } NSQL_REQ::NSQL_REQ() { buf.reserve(1000); } void NSQL_REQ::clear() { buf.clear(); } void NSQL_REQ::append (PARAM_STRING s) { buf += s.ptr; } void NSQL_REQ::append (const char car) { buf += car; } void NSQL_REQ::append_arg (PARAM_STRING s) { buf += '\''; const char *pt = s.ptr; while (*pt != '\0'){ const char car = *pt++; if (car == '\\' || car == '\'' || car == '"'){ buf += '\\'; } buf += car; } buf += '\''; } void NSQL_REQ::append_arg (const unsigned val) { buf += string_f("%u",val); } void NSQL_REQ::append_arg (const int val) { buf += string_f("%d",val); } void NSQL_REQ::appendf (const char *ctl, ...) { va_list list; va_start (list,ctl); nsql_vsnprintf (buf,ctl,list); va_end (list); } const char *NSQL_REQ::c_str() const { return buf.c_str(); } size_t NSQL_REQ::size() const { return buf.size(); }