Skip to content

Commit

Permalink
Merge pull request #73 from vadz/fb-dsn-parse
Browse files Browse the repository at this point in the history
Fix connection parsing in SOCI Firebird backend to handle spaces in values.
  • Loading branch information
vnaydionov committed Feb 18, 2013
2 parents 9b7ab2d + 2706859 commit 4309f19
Showing 1 changed file with 144 additions and 15 deletions.
159 changes: 144 additions & 15 deletions src/backends/firebird/session.cpp
Expand Up @@ -9,6 +9,7 @@
#include "soci-firebird.h"
#include "error-firebird.h"
#include "session.h"
#include <locale>
#include <map>
#include <sstream>
#include <string>
Expand All @@ -19,32 +20,160 @@ using namespace soci::details::firebird;
namespace
{

// retrieves parameters from the uniform connect string
void explodeISCConnectString(std::string const &connectString,
std::map<std::string, std::string> &parameters)
// Helpers of explodeISCConnectString() for reading words from a string. "Word"
// here is defined very loosely as just a sequence of non-space characters.
//
// All these helper functions update the input iterator to point to the first
// character not consumed by them.

// Advance the input iterator until the first non-space character or end of the
// string.
void skipWhiteSpace(std::string::const_iterator& i, std::string::const_iterator const &end)
{
std::string tmp;
for (std::string::const_iterator i = connectString.begin(),
end = connectString.end(); i != end; ++i)
std::locale const loc;
for (; i != end; ++i)
{
if (*i == '=')
if (!std::isspace(*i, loc))
break;
}
}

// Return the string of all characters until the first space or the specified
// delimiter.
//
// Throws if the first non-space character after the end of the word is not the
// delimiter. However just returns en empty string, without throwing, if
// nothing is left at all in the string except for white space.
std::string
getWordUntil(std::string const &s, std::string::const_iterator &i, char delim)
{
std::string::const_iterator const end = s.end();
skipWhiteSpace(i, end);

// We need to handle this case specially because it's not an error if
// nothing at all remains in the string. But if anything does remain, then
// we must have the delimiter.
if (i == end)
return std::string();

// Simply put anything until the delimiter into the word, stopping at the
// first white space character.
std::string word;
std::locale const loc;
for (; i != end; ++i)
{
if (*i == delim)
break;

if (std::isspace(*i, loc))
{
tmp += ' ';
skipWhiteSpace(i, end);
if (i == end || *i != delim)
{
std::ostringstream os;
os << "Expected '" << delim << "' at position "
<< (i - s.begin() + 1)
<< " in Firebird connection string \""
<< s << "\".";

throw soci_error(os.str());
}

break;
}
else

word += *i;
}

if (i == end)
{
std::ostringstream os;
os << "Expected '" << delim
<< "' not found before the end of the string "
<< "in Firebird connection string \""
<< s << "\".";

throw soci_error(os.str());
}

++i; // Skip the delimiter itself.

return word;
}

// Return a possibly quoted word, i.e. either just a sequence of non-space
// characters or everything inside a double-quoted string.
//
// Throws if the word is quoted and the closing quote is not found. However
// doesn't throw, just returns an empty string if there is nothing left.
std::string
getPossiblyQuotedWord(std::string const &s, std::string::const_iterator &i)
{
std::string::const_iterator const end = s.end();
skipWhiteSpace(i, end);

std::string word;

if (i != end && *i == '"')
{
for (;;)
{
tmp += *i;
if (++i == end)
{
std::ostringstream os;
os << "Expected '\"' not found before the end of the string "
"in Firebird connection string \""
<< s << "\".";

throw soci_error(os.str());
}

if (*i == '"')
{
++i;
break;
}

word += *i;
}
}
else // Not quoted.
{
std::locale const loc;
for (; i != end; ++i)
{
if (std::isspace(*i, loc))
break;

parameters.clear();
word += *i;
}
}

return word;
}

// retrieves parameters from the uniform connect string which is supposed to be
// in the form "key=value[ key2=value2 ...]" and the values may be quoted to
// allow including spaces into them. Notice that currently there is no way to
// include both a space and a double quote in a value.
std::map<std::string, std::string>
explodeISCConnectString(std::string const &connectString)
{
std::map<std::string, std::string> parameters;

std::istringstream iss(tmp);
std::string key, value;
while (iss >> key >> value)
for (std::string::const_iterator i = connectString.begin(); ; )
{
key = getWordUntil(connectString, i, '=');
if (key.empty())
break;

value = getPossiblyQuotedWord(connectString, i);

parameters.insert(std::pair<std::string, std::string>(key, value));
}

return parameters;
}

// extracts given parameter from map previusly build with explodeISCConnectString
Expand Down Expand Up @@ -74,8 +203,8 @@ firebird_session_backend::firebird_session_backend(
, decimals_as_strings_(false)
{
// extract connection parameters
std::map<std::string, std::string> params;
explodeISCConnectString(connectString, params);
std::map<std::string, std::string>
params(explodeISCConnectString(connectString));

ISC_STATUS stat[stat_size];
std::string param;
Expand Down

0 comments on commit 4309f19

Please sign in to comment.