Re: libpq bug?

2018-12-28 Thread patrick keshishian
On Fri, Dec 28, 2018 at 2:00 PM Igor Korot  wrote:

> Hi, ALL,
> Following code:
>
> int PostgresDatabase::GetTableOwner (const std::wstring &schemaName,
> const std::wstring &tableName, std::wstring &owner,
> std::vector &errorMsg)
> {
>int result = 0;
> std::wstring query = L"SELECT u.usename FROM pg_class c, pg_user
> u, pg_namespace n WHERE n.oid = c.relnamespace AND u.usesysid =
> c.relowner AND n.nspname = $1 AND relname = $2";
> char *values[2];
> values[0] = NULL, values[1] = NULL;
> values[0] = new char[schemaName.length() + 1];
> values[1] = new char[tableName.length() + 1];
> memset( values[0], '\0', schemaName.length() + 1 );
> memset( values[1], '\0', tableName.length() + 1 );
> strcpy( values[0], m_pimpl->m_myconv.to_bytes( schemaName.c_str()
> ).c_str() );
> strcpy( values[1], m_pimpl->m_myconv.to_bytes( tableName.c_str()
> ).c_str() );
> int len1 = (int) schemaName.length();
> int len2 = (int) tableName.length();
> int length[2] = { len1, len2 };
> int formats[2] = { 1, 1 };
> PGresult *res = PQexecParams( m_db, m_pimpl->m_myconv.to_bytes(
> query.c_str() ).c_str(), 2, NULL, values, length, formats, 1 );
> ExecStatusType status = PQresultStatus( res );
> if( status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK )
> {
> result = 1;
> std::wstring err = m_pimpl->m_myconv.from_bytes(
> PQerrorMessage( m_db ) );
> errorMsg.push_back( L"Error executing query: " + err );
> PQclear( res );
> }
> else
> {
> owner = m_pimpl->m_myconv.from_bytes( PQgetvalue( res, 0, 0 ) );
> }
> return result;
> }
>
> when ran with the call of
>
> GetTableOwner( "public", "abcß", owner, errorMsg );
>
> returns:
>
> ERROR: Invalid byte sequence for encoding UTF8.
>
> Does this mean I found the bug in the library?
>

The bug is in your C++ code. "abcß" as tableName.lenght() (wstring) returns
4 (as in four characters) not number of bytes required to represent the
intended string: 61 62 63 c3 9f
Since the last character is a 2 bytes in length. Therefore, your call to
PQexecParams() specifies a shorter length and hence an invalid UTF-8
sequence.

Furthermore, your value[] array allocation is in error since
wstring::length returns number of characters, not number of bytes. so you
will end up with buffer-overflows.

HTH,
--patrick



> Any idea what I can do?
>
> Thank you.
>
>


Re: libpq bug?

2018-12-28 Thread patrick keshishian
On Fri, Dec 28, 2018 at 3:07 PM Igor Korot  wrote:

> Hi,
>
> On Fri, Dec 28, 2018 at 4:51 PM patrick keshishian 
> wrote:
> >
> >
> > On Fri, Dec 28, 2018 at 2:00 PM Igor Korot  wrote:
> >>
> >> Hi, ALL,
> >> Following code:
> >>
> >> int PostgresDatabase::GetTableOwner (const std::wstring &schemaName,
> >> const std::wstring &tableName, std::wstring &owner,
> >> std::vector &errorMsg)
> >> {
> >>int result = 0;
> >> std::wstring query = L"SELECT u.usename FROM pg_class c, pg_user
> >> u, pg_namespace n WHERE n.oid = c.relnamespace AND u.usesysid =
> >> c.relowner AND n.nspname = $1 AND relname = $2";
> >> char *values[2];
> >> values[0] = NULL, values[1] = NULL;
> >> values[0] = new char[schemaName.length() + 1];
> >> values[1] = new char[tableName.length() + 1];
> >> memset( values[0], '\0', schemaName.length() + 1 );
> >> memset( values[1], '\0', tableName.length() + 1 );
> >> strcpy( values[0], m_pimpl->m_myconv.to_bytes( schemaName.c_str()
> >> ).c_str() );
> >> strcpy( values[1], m_pimpl->m_myconv.to_bytes( tableName.c_str()
> >> ).c_str() );
> >> int len1 = (int) schemaName.length();
> >> int len2 = (int) tableName.length();
> >> int length[2] = { len1, len2 };
> >> int formats[2] = { 1, 1 };
> >> PGresult *res = PQexecParams( m_db, m_pimpl->m_myconv.to_bytes(
> >> query.c_str() ).c_str(), 2, NULL, values, length, formats, 1 );
> >> ExecStatusType status = PQresultStatus( res );
> >> if( status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK )
> >> {
> >> result = 1;
> >> std::wstring err = m_pimpl->m_myconv.from_bytes(
> >> PQerrorMessage( m_db ) );
> >> errorMsg.push_back( L"Error executing query: " + err );
> >> PQclear( res );
> >> }
> >> else
> >> {
> >> owner = m_pimpl->m_myconv.from_bytes( PQgetvalue( res, 0, 0 ) );
> >> }
> >> return result;
> >> }
> >>
> >> when ran with the call of
> >>
> >> GetTableOwner( "public", "abcß", owner, errorMsg );
> >>
> >> returns:
> >>
> >> ERROR: Invalid byte sequence for encoding UTF8.
> >>
> >> Does this mean I found the bug in the library?
> >
> >
> > The bug is in your C++ code. "abcß" as tableName.lenght() (wstring)
> returns 4 (as in four characters) not number of bytes required to represent
> the intended string: 61 62 63 c3 9f
> > Since the last character is a 2 bytes in length. Therefore, your call to
> PQexecParams() specifies a shorter length and hence an invalid UTF-8
> sequence.
> >
> > Furthermore, your value[] array allocation is in error since
> wstring::length returns number of characters, not number of bytes. so you
> will end up with buffer-overflows.
>
> So I should use
>
> https://stackoverflow.com/questions/9278723/how-can-i-get-the-byte-size-of-stdwstring
> in both places?
>

size() also returns 4. If you multiply it with sizeof(wchar_t) you will end
up with maximum buffers size necessary to hold the string (minus
terminating \0), but not the correct length you are after. I am unsure of
the "correct" C++ solution.

Sorry,
--patrick



> Thank you.
>
> >
> > HTH,
> > --patrick
> >
> >
> >>
> >> Any idea what I can do?
> >>
> >> Thank you.
> >>
>


Re: libpq bug?

2018-12-28 Thread patrick keshishian
On Fri, Dec 28, 2018 at 5:40 PM Igor Korot  wrote:

> Hi, Patrick,
>
> Here is my new code:
>
> int PostgresDatabase::GetTableOwner (const std::wstring &schemaName,
> const std::wstring &tableName, std::wstring &owner,
> std::vector &errorMsg)
> {
> int result = 0;
> std::wstring query = L"SELECT u.usename FROM pg_class c, pg_user
> u, pg_namespace n WHERE n.oid = c.relnamespace AND u.usesysid =
> c.relowner AND n.nspname = $1 AND relname = $2";
> char *values[2];
> values[0] = NULL, values[1] = NULL;
> int charlength1 = schemaName.length() * sizeof( wchar_t ),
> charlength2 = tableName.length() * sizeof( wchar_t );
> values[0] = new char[schemaName.length() * sizeof( wchar_t ) + 1];
> values[1] = new char[tableName.length() * sizeof( wchar_t ) + 1];
> memset( values[0], '\0', schemaName.length()  * sizeof( wchar_t ) + 1
> );
> memset( values[1], '\0', tableName.length() * sizeof( wchar_t ) + 1 );
> strcpy( values[0], m_pimpl->m_myconv.to_bytes( schemaName.c_str()
> ).c_str() );
> strcpy( values[1], m_pimpl->m_myconv.to_bytes( tableName.c_str()
> ).c_str() );
> int len1 = (int) schemaName.length() * sizeof( wchar_t );
> int len2 = (int) tableName.length() * sizeof( wchar_t );
> int length[2] = { len1, len2 };
> int formats[2] = { 1, 1 };
> PGresult *res = PQexecParams( m_db, m_pimpl->m_myconv.to_bytes(
> query.c_str() ).c_str(), 2, NULL, values, length, formats, 1 );
> ExecStatusType status = PQresultStatus( res );
> if( status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK )
> {
> result = 1;
> std::wstring err = m_pimpl->m_myconv.from_bytes(
> PQerrorMessage( m_db ) );
> errorMsg.push_back( L"Error executing query: " + err );
> PQclear( res );
> }
> else
> {
> owner = m_pimpl->m_myconv.from_bytes( PQgetvalue( res, 0, 0 ) );
> }
> return result;
> }
>
> The charlength2 variable contains the value of 8 and I'm still getting
> the same error.


I was hoping someone more versed in C++ would jump in to answer your
question. I haven't used C++ in at least a decade.
You need to convert the wchar_t  data that wstring stores into UTF-8.
Personally, I would use iconv (common enough).

I assume the PostgresDatabase class is your own (?) I would add a helper
function to do the conversion. Here is a very rough template for you to
adapt if you think it helps you.


#include 

#include 

#include 

#include 

#include 


#include 


class PGDB {

public:

// your stuff ...

iconv_t ic;


PGDB(void) {

setlocale(LC_CTYPE, "");

ic = iconv_open("UTF-8", "wchar_t");

if ((iconv_t)-1 == ic)

errx(1, "iconv_open");

}

~PGDB() {

iconv_close(ic);

}

// caller should free()

char*wchar2utf8(std::wstring const &ws) {

char*in, *buf, *out;

size_t  bufsz, inbytes, outbytes;


in = (char *)ws.data();

inbytes = ws.length() * sizeof(wchar_t);

outbytes = inbytes;

bufsz = inbytes + 1; // XXX check for overflow


buf = (char *)calloc(bufsz, 1);

if (NULL == buf)

err(1, NULL); // or throw something


out = buf;

if ((size_t)-1 == iconv(ic, &in, &inbytes, &out, &outbytes))

errx(1, "iconv"); // or throw ...


// TODO ensure inbytes is 0 (meaning all input consumed)

return buf;

}

};


// demo using above PGDB class/code

int main(int argc, char *argv[])

{

char*str;

size_t  i, n;

std::wstringtab;

PGDBpg;


tab = L"ºabcß";

str = pg.wchar2utf8(tab);


n = strlen(str);

for (i = 0; i < n; ++i) {

printf("%02hhx ", str[i]);

}

printf("\n");

printf("->%s<-\n", str);


free(str);

return 0;

}


So in GetTableOwner() you'd call wchar2utf8() for the input wstring data
and you don't do the new/memset/strcpy. Just remember to free() the
returned utf8 string pointers after call to PQexecParams().

cheers,
--patrick




> Any idea?
>
> Thank you.
>
> On Fri, Dec 28, 2018 at 5:40 PM patrick keshishian 
> wrote:
> >
> > On Fri, Dec 28, 2018 at 3:07 PM Igor Korot  wrote:
> >>
> >> Hi,
> >>
> >> On Fri, Dec 28, 2