merge android_branch->

This commit is contained in:
Eric House 2014-04-25 18:47:24 -07:00
commit f954b09391
7 changed files with 278 additions and 131 deletions

View file

@ -38,6 +38,7 @@ SRC = \
udpager.cpp \ udpager.cpp \
udpqueue.cpp \ udpqueue.cpp \
xwrelay.cpp \ xwrelay.cpp \
querybld.cpp \
# STATIC ?= -static # STATIC ?= -static
GITINFO = gitversion.txt GITINFO = gitversion.txt

View file

@ -43,11 +43,8 @@
static DBMgr* s_instance = NULL; static DBMgr* s_instance = NULL;
#define DELIM "\1"
#define MAX_NUM_PLAYERS 4 #define MAX_NUM_PLAYERS 4
static void formatParams( char* paramValues[], int nParams, const char* fmt,
char* buf, int bufLen, ... );
static int here_less_seed( const char* seeds, int perDeviceSum, static int here_less_seed( const char* seeds, int perDeviceSum,
unsigned short seed ); unsigned short seed );
static void destr_function( void* conn ); static void destr_function( void* conn );
@ -105,20 +102,21 @@ DBMgr::AddNew( const char* cookie, const char* connName, CookieID cid,
if ( !cookie ) cookie = ""; if ( !cookie ) cookie = "";
if ( !connName ) connName = ""; if ( !connName ) connName = "";
const char* command = "INSERT INTO " GAMES_TABLE QueryBuilder qb;
qb.appendQueryf( "INSERT INTO " GAMES_TABLE
" (cid, room, connName, nTotal, lang, pub)" " (cid, room, connName, nTotal, lang, pub)"
" VALUES( $1, $2, $3, $4, $5, $6 )"; " VALUES( $$, $$, $$, $$, $$, $$ )" )
int nParams = 6; .appendParam(cid)
char* paramValues[nParams]; .appendParam(cookie)
char buf[512]; .appendParam(connName)
formatParams( paramValues, nParams, .appendParam(nPlayersT)
"%d"DELIM"%s"DELIM"%s"DELIM"%d"DELIM"%d"DELIM"%s", .appendParam(langCode)
buf, sizeof(buf), cid, cookie, connName, nPlayersT, .appendParam(isPublic?"TRUE":"FALSE" )
langCode, isPublic?"TRUE":"FALSE" ); .finish();
PGresult* result = PQexecParams( getThreadConn(), command, PGresult* result = PQexecParams( getThreadConn(), qb.c_str(),
nParams, NULL, qb.paramCount(), NULL,
paramValues, qb.paramValues(),
NULL, NULL, 0 ); NULL, NULL, 0 );
if ( PGRES_COMMAND_OK != PQresultStatus(result) ) { if ( PGRES_COMMAND_OK != PQresultStatus(result) ) {
logf( XW_LOGERROR, "PQexec=>%s;%s", PQresStatus(PQresultStatus(result)), logf( XW_LOGERROR, "PQexec=>%s;%s", PQresStatus(PQresultStatus(result)),
@ -271,28 +269,27 @@ DBMgr::SeenSeed( const char* cookie, unsigned short seed,
char* connNameBuf, int bufLen, int* nPlayersHP, char* connNameBuf, int bufLen, int* nPlayersHP,
CookieID* cid ) CookieID* cid )
{ {
int nParams = 5; QueryBuilder qb;
char* paramValues[nParams]; qb.appendQueryf( "SELECT cid, connName, seeds, sum_array(nPerDevice) FROM "
char buf[512];
formatParams( paramValues, nParams,
"%s"DELIM"%d"DELIM"%d"DELIM"%d"DELIM"%s", buf, sizeof(buf),
cookie, langCode, nPlayersT, seed,
wantsPublic?"TRUE":"FALSE" );
const char* cmd = "SELECT cid, connName, seeds, sum_array(nPerDevice) FROM "
GAMES_TABLE GAMES_TABLE
" WHERE NOT dead" " WHERE NOT dead"
" AND room ILIKE $1" " AND room ILIKE $$"
" AND lang = $2" " AND lang = $$"
" AND nTotal = $3" " AND nTotal = $$"
" AND $4 = ANY(seeds)" " AND $$ = ANY(seeds)"
" AND $5 = pub" " AND $$ = pub"
" ORDER BY ctime DESC" " ORDER BY ctime DESC"
" LIMIT 1"; " LIMIT 1")
.appendParam(cookie)
.appendParam(langCode)
.appendParam(nPlayersT)
.appendParam(seed)
.appendParam(wantsPublic?"TRUE":"FALSE" )
.finish();
PGresult* result = PQexecParams( getThreadConn(), cmd, PGresult* result = PQexecParams( getThreadConn(), qb.c_str(),
nParams, NULL, qb.paramCount(), NULL,
paramValues, qb.paramValues(),
NULL, NULL, 0 ); NULL, NULL, 0 );
bool found = 1 == PQntuples( result ); bool found = 1 == PQntuples( result );
if ( found ) { if ( found ) {
@ -312,31 +309,28 @@ DBMgr::FindOpen( const char* cookie, int lang, int nPlayersT, int nPlayersH,
bool wantsPublic, char* connNameBuf, int bufLen, bool wantsPublic, char* connNameBuf, int bufLen,
int* nPlayersHP ) int* nPlayersHP )
{ {
CookieID cid = 0; QueryBuilder qb;
qb.appendQueryf("SELECT cid, connName, sum_array(nPerDevice) FROM "
int nParams = 5;
char* paramValues[nParams];
char buf[512];
formatParams( paramValues, nParams,
"%s"DELIM"%d"DELIM"%d"DELIM"%d"DELIM"%s", buf, sizeof(buf),
cookie, lang, nPlayersT, nPlayersH, wantsPublic?"TRUE":"FALSE" );
/* NOTE: ILIKE, for case-insensitive comparison, is a postgres extension
to SQL. */
const char* cmd = "SELECT cid, connName, sum_array(nPerDevice) FROM "
GAMES_TABLE GAMES_TABLE
" WHERE NOT dead" " WHERE NOT dead"
" AND room ILIKE $1" " AND room ILIKE $$"
" AND lang = $2" " AND lang = $$"
" AND nTotal = $3" " AND nTotal = $$"
" AND $4 <= nTotal-sum_array(nPerDevice)" " AND $$ <= nTotal-sum_array(nPerDevice)"
" AND $5 = pub" " AND $$ = pub"
" LIMIT 1"; " LIMIT 1")
.appendParam(cookie)
.appendParam(lang)
.appendParam(nPlayersT)
.appendParam(nPlayersH)
.appendParam(wantsPublic?"TRUE":"FALSE" )
.finish();
PGresult* result = PQexecParams( getThreadConn(), cmd, PGresult* result = PQexecParams( getThreadConn(), qb.c_str(),
nParams, NULL, qb.paramCount(), NULL,
paramValues, qb.paramValues(),
NULL, NULL, 0 ); NULL, NULL, 0 );
CookieID cid = 0;
if ( 1 == PQntuples( result ) ) { if ( 1 == PQntuples( result ) ) {
cid = atoi( PQgetvalue( result, 0, 0 ) ); cid = atoi( PQgetvalue( result, 0, 0 ) );
snprintf( connNameBuf, bufLen, "%s", PQgetvalue( result, 0, 1 ) ); snprintf( connNameBuf, bufLen, "%s", PQgetvalue( result, 0, 1 ) );
@ -396,14 +390,21 @@ DBMgr::RegisterDevice( const DevID* host, int clientVersion,
devID = (DevIDRelay)random(); devID = (DevIDRelay)random();
} while ( DEVID_NONE == devID ); } while ( DEVID_NONE == devID );
StrWPF query; QueryBuilder qb;
query.catf( "INSERT INTO " DEVICES_TABLE " (id, devTypes[1]," qb.appendQueryf( "INSERT INTO " DEVICES_TABLE " (id, devTypes[1],"
" devids[1], clntVers, versdesc, model, osvers)" " devids[1], clntVers, versdesc, model, osvers)"
" VALUES( %d, %d, '%s', %d, '%s', '%s', '%s' )", " VALUES($$, $$, $$, $$, $$, $$, $$)" );
devID, host->m_devIDType, devidStr, clientVersion,
desc, model, osVers ); qb.appendParam( devID )
logf( XW_LOGINFO, "%s: %s", __func__, query.c_str() ); .appendParam( host->m_devIDType )
success = execSql( query ); .appendParam( devidStr )
.appendParam( clientVersion )
.appendParam( desc )
.appendParam( model )
.appendParam( osVers )
.finish();
success = execParams( qb );
} }
} }
return devID; return devID;
@ -420,15 +421,17 @@ DBMgr::ReregisterDevice( DevIDRelay relayID, const DevID* host,
const char* const desc, int clientVersion, const char* const desc, int clientVersion,
const char* const model, const char* const osVers ) const char* const model, const char* const osVers )
{ {
// First update the existing QueryBuilder qb;
StrWPF query; qb.appendQueryf( "UPDATE " DEVICES_TABLE " SET "
query.catf( "UPDATE " DEVICES_TABLE " SET " "devTypes = array_prepend($$, devTypes), "
"devTypes = array_prepend( %d, devTypes), " "devids = array_prepend($$, devids), " )
"devids = array_prepend('%s', devids), ",
host->m_devIDType, host->m_devIDString.c_str() );
formatUpdate( query, true, desc, clientVersion, model, osVers, relayID ); .appendParam( host->m_devIDType )
execSql( query ); .appendParam( host->m_devIDString.c_str() );
formatUpdate( qb, true, desc, clientVersion, model, osVers, relayID );
qb.finish();
execParams( qb );
} }
// Return true if the relayID exists in the DB already // Return true if the relayID exists in the DB already
@ -445,10 +448,11 @@ DBMgr::UpdateDevice( DevIDRelay relayID, const char* const desc,
} }
if ( exists ) { if ( exists ) {
StrWPF query; QueryBuilder qb;
query.catf( "UPDATE " DEVICES_TABLE " SET " ); qb.appendQueryf( "UPDATE " DEVICES_TABLE " SET " );
formatUpdate( query, false, desc, clientVersion, model, osVers, relayID ); formatUpdate( qb, false, desc, clientVersion, model, osVers, relayID );
execSql( query ); qb.finish();
execParams( qb );
} }
return exists; return exists;
} }
@ -460,26 +464,33 @@ DBMgr::UpdateDevice( DevIDRelay relayID )
} }
void void
DBMgr::formatUpdate( StrWPF& query, bool append, const char* const desc, DBMgr::formatUpdate( QueryBuilder& qb,
bool append, const char* const desc,
int clientVersion, const char* const model, int clientVersion, const char* const model,
const char* const osVers, DevIDRelay relayID ) const char* const osVers, DevIDRelay relayID )
{ {
if ( append ) { if ( append ) {
query.catf( "mtimes=array_prepend('now', mtimes)" ); // FIXME: too many qb.appendQueryf( "mtimes=array_prepend('now', mtimes)" ); // FIXME: too many
} else { } else {
query.catf( "mtimes[1]='now'" ); qb.appendQueryf( "mtimes[1]='now'" );
} }
if ( NULL != desc && '\0' != desc[0] ) { if ( NULL != desc && '\0' != desc[0] ) {
query.catf( ", clntVers=%d, versDesc='%s'", clientVersion, desc ); qb.appendQueryf( ", clntVers=$$" )
.appendParam( clientVersion )
.appendQueryf( ", versDesc=$$" )
.appendParam( desc );
} }
if ( NULL != model && '\0' != model[0] ) { if ( NULL != model && '\0' != model[0] ) {
query.catf( ", model='%s'", model ); qb.appendQueryf( ", model=$$" )
.appendParam( model );
} }
if ( NULL != osVers && '\0' != osVers[0] ) { if ( NULL != osVers && '\0' != osVers[0] ) {
query.catf( ", osvers='%s'", osVers ); qb.appendQueryf( ", osvers=$$" )
.appendParam( osVers );
} }
query.catf( " WHERE id = %d", relayID ); qb.appendQueryf( " WHERE id = $$" )
.appendParam( relayID );
} }
HostID HostID
@ -831,6 +842,22 @@ DBMgr::execSql( const char* const query )
return ok; return ok;
} }
bool
DBMgr::execParams( QueryBuilder& qb )
{
PGresult* result = PQexecParams( getThreadConn(), qb.c_str(),
qb.paramCount(), NULL,
qb.paramValues(),
NULL, NULL, 0 );
bool success = PGRES_COMMAND_OK == PQresultStatus( result );
if ( !success ) {
logf( XW_LOGERROR, "PQexecParams(%s)=>%s;%s", qb.c_str(),
PQresStatus(PQresultStatus(result)),
PQresultErrorMessage(result) );
}
return success;
}
void void
DBMgr::readArray( const char* const connName, const char* column, int arr[] ) /* len 4 */ DBMgr::readArray( const char* const connName, const char* column, int arr[] ) /* len 4 */
{ {
@ -1255,31 +1282,6 @@ void DBMgr::clearHasNoMessages( DevIDRelay devid )
assert( !hasNoMessages( devid ) ); assert( !hasNoMessages( devid ) );
} }
static void
formatParams( char* paramValues[], int nParams, const char* fmt, char* buf,
int bufLen, ... )
{
va_list ap;
va_start( ap, bufLen );
int len = vsnprintf( buf, bufLen, fmt, ap );
assert( buf[len] == '\0' );
int pnum;
char* ptr = buf;
for ( pnum = 0; pnum < nParams; ++pnum ) {
paramValues[pnum] = ptr;
for ( ; *ptr != '\0' && *ptr != DELIM[0]; ++ptr ) {
// do nothing
assert( ptr < &buf[bufLen] );
}
// we've found an end
*ptr = '\0';
++ptr;
}
va_end(ap);
}
static int static int
here_less_seed( const char* seeds, int sumPerDevice, unsigned short seed ) here_less_seed( const char* seeds, int sumPerDevice, unsigned short seed )
{ {

View file

@ -32,6 +32,7 @@
#include "xwrelay_priv.h" #include "xwrelay_priv.h"
#include "devid.h" #include "devid.h"
#include "strwpf.h" #include "strwpf.h"
#include "querybld.h"
using namespace std; using namespace std;
@ -149,6 +150,7 @@ class DBMgr {
DBMgr(); DBMgr();
bool execSql( const string& query ); bool execSql( const string& query );
bool execSql( const char* const query ); /* no-results query */ bool execSql( const char* const query ); /* no-results query */
bool execParams( QueryBuilder& qb );
void readArray( const char* const connName, const char* column, int arr[] ); void readArray( const char* const connName, const char* column, int arr[] );
DevIDRelay getDevID( const char* connName, int hid ); DevIDRelay getDevID( const char* connName, int hid );
DevIDRelay getDevID( const DevID* devID ); DevIDRelay getDevID( const DevID* devID );
@ -160,7 +162,7 @@ class DBMgr {
bool nullConnnameOK ); bool nullConnnameOK );
int CountStoredMessages( const char* const connName, int hid ); int CountStoredMessages( const char* const connName, int hid );
bool UpdateDevice( DevIDRelay relayID ); bool UpdateDevice( DevIDRelay relayID );
void formatUpdate( StrWPF& query, bool append, const char* const desc, void formatUpdate( QueryBuilder& qb, bool append, const char* const desc,
int clientVersion, const char* const model, int clientVersion, const char* const model,
const char* const osVers, DevIDRelay relayID ); const char* const osVers, DevIDRelay relayID );

View file

@ -0,0 +1,78 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2014 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdarg.h>
#include "querybld.h"
#include "xwrelay_priv.h"
QueryBuilder&
QueryBuilder::appendQueryf( const char* fmt, ... )
{
bool done;
do {
va_list ap;
va_start( ap, fmt );
done = m_query.catf( fmt, ap );
va_end( ap );
} while ( !done );
return *this;
}
QueryBuilder&
QueryBuilder::appendParam( const char* value )
{
m_paramIndices.push_back( m_paramBuf.size() );
m_paramBuf.catf( "%s%c", value, '\0' );
return *this;
}
QueryBuilder&
QueryBuilder::appendParam( int value )
{
m_paramIndices.push_back( m_paramBuf.size() );
m_paramBuf.catf( "%d%c", value, '\0' );
return *this;
}
/* When done adding params, some of which contain $$, turn these into an order
* progression of $1, $2 .. $9. Note assumption that we don't go above 9 since
*/
void
QueryBuilder::finish()
{
assert( 0 == m_paramValues.size() );
size_t ii;
const char* base = m_paramBuf.c_str();
for ( ii = 0; ii < m_paramIndices.size(); ++ii ) {
const char* ptr = m_paramIndices[ii] + base;
m_paramValues.push_back( ptr );
}
for ( size_t count = 0; ; ++count ) {
const char* str = m_query.c_str();
const char* ptr = strstr( str, "$$" );
if ( !ptr ) {
assert( count == m_paramIndices.size() );
break;
}
assert( count < 9 );
m_query[1 + ptr - str] = '1' + count;
}
}

47
xwords4/relay/querybld.h Normal file
View file

@ -0,0 +1,47 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/*
* Copyright 2014 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QUERYBLD_H_
#define _QUERYBLD_H_
#include <vector>
#include "strwpf.h"
using namespace std;
class QueryBuilder {
public:
QueryBuilder& appendQueryf( const char* fmt, ... );
QueryBuilder& appendParam( const char* value );
QueryBuilder& appendParam( int value );
void finish();
int paramCount() const { return m_paramValues.size(); }
const char* const* paramValues() const { return &m_paramValues[0]; }
const char* const c_str() const { return m_query.c_str(); }
private:
StrWPF m_query;
StrWPF m_paramBuf;
vector<size_t> m_paramIndices;
vector<const char*> m_paramValues;
};
#endif

View file

@ -27,26 +27,37 @@
/* From stack overflow: snprintf with an expanding buffer. /* From stack overflow: snprintf with an expanding buffer.
*/ */
void bool
StrWPF::catf( const char* fmt, ... ) StrWPF::catf( const char* fmt, va_list ap )
{ {
bool success = false;
const int origsiz = size(); const int origsiz = size();
int addsiz = 100; resize( origsiz + m_addsiz );
va_list ap;
for ( ; ; ) {
resize( origsiz + addsiz );
va_start( ap, fmt ); int len = vsnprintf( (char*)c_str() + origsiz, m_addsiz, fmt, ap );
int len = vsnprintf( (char *)c_str() + origsiz, addsiz, fmt, ap );
va_end( ap );
if ( len >= addsiz ) { // needs more space if ( len >= m_addsiz ) { // needs more space
addsiz = len + 1; m_addsiz = len + 1;
resize( origsiz );
} else if ( -1 == len ) { } else if ( -1 == len ) {
assert(0); // should be impossible assert(0); // should be impossible
} else { } else {
resize( origsiz + len ); resize( origsiz + len );
break; m_addsiz = 100;
} success = true;
} }
return success;
}
void
StrWPF::catf( const char* fmt, ... )
{
bool done;
do {
va_list ap;
va_start( ap, fmt );
done = catf( fmt, ap );
va_end( ap );
} while ( !done );
} }

View file

@ -21,10 +21,16 @@
#define _STRWPF_H_ #define _STRWPF_H_
#include <string> #include <string>
#include <stdarg.h>
class StrWPF : public std::string { class StrWPF : public std::string {
public: public:
StrWPF() : m_addsiz(100){}
void catf( const char* fmt, ... ); void catf( const char* fmt, ... );
bool catf( const char* fmt, va_list ap );
private:
int m_addsiz;
}; };
#endif #endif