Skip to content

Commit

Permalink
Fixes for using none ascii characters in Sql Server connection string…
Browse files Browse the repository at this point in the history
…s, queries and results (#6150)

* Fix encoding issues in SQL Server plugin

There has been an encoding problem in the SQL Server plugin. Queries used SQLExecDirect instead of SQLExecDirectW making the plugin dependent on that the systems locale matched the encoding of the map-file. For Swedish characters on Swedish Windows the map file needed to be in ISO-8859-1 but other connections such as PostGIS reguire the map-file to be in UTF-8. So combining different type of connections was a problem. The plugin was also using a bad pattern when reading attributes from SQL Server, see https://docs.microsoft.com/en-us/sql/relational-databases/native-client/features/odbc-driver-behavior-change-when-handling-character-conversions?view=sql-server-ver15

* Update mapmssql2008.c

Added support for unicode characters in SQL Server connection string.

* Fixed casting in msConvertWideStringFromUTF8.
  • Loading branch information
Krister Wicksell committed Sep 29, 2020
1 parent cb22172 commit d91fec4
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 21 deletions.
88 changes: 67 additions & 21 deletions mapmssql2008.c
Expand Up @@ -740,7 +740,6 @@ static void setConnError(msODBCconn *conn)
/* Connect to db */
static msODBCconn * mssql2008Connect(char * connString)
{
SQLCHAR outConnString[1024];
SQLSMALLINT outConnStringLen;
SQLRETURN rc;
msODBCconn * conn = msSmallMalloc(sizeof(msODBCconn));
Expand All @@ -759,12 +758,28 @@ static msODBCconn * mssql2008Connect(char * connString)

snprintf((char*)fullConnString, sizeof(fullConnString), "DRIVER={SQL Server};%s", connString);

#ifdef USE_ICONV
wchar_t *decodedConnString = msConvertWideStringFromUTF8(fullConnString, "UCS-2LE");
wchar_t outConnString[1024];
rc = SQLDriverConnectW(conn->hdbc, NULL, decodedConnString, SQL_NTS, outConnString, 1024, &outConnStringLen, SQL_DRIVER_NOPROMPT);
msFree(decodedConnString);
#else
SQLCHAR outConnString[1024];
rc = SQLDriverConnect(conn->hdbc, NULL, fullConnString, SQL_NTS, outConnString, 1024, &outConnStringLen, SQL_DRIVER_NOPROMPT);
#endif
}
else
{
#ifdef USE_ICONV
wchar_t *decodedConnString = msConvertWideStringFromUTF8(connString, "UCS-2LE");
wchar_t outConnString[1024];
rc = SQLDriverConnectW(conn->hdbc, NULL, decodedConnString, SQL_NTS, outConnString, 1024, &outConnStringLen, SQL_DRIVER_NOPROMPT);
msFree(decodedConnString);
#else
SQLCHAR outConnString[1024];
rc = SQLDriverConnect(conn->hdbc, NULL, (SQLCHAR*)connString, SQL_NTS, outConnString, 1024, &outConnStringLen, SQL_DRIVER_NOPROMPT);
}
#endif
}

if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
setConnError(conn);
Expand Down Expand Up @@ -794,7 +809,13 @@ static int executeSQL(msODBCconn *conn, const char * sql)

SQLCloseCursor(conn->hstmt);

#ifdef USE_ICONV
wchar_t *decodedSql = msConvertWideStringFromUTF8(sql, "UCS-2LE");
rc = SQLExecDirectW(conn->hstmt, decodedSql, SQL_NTS);
msFree(decodedSql);
#else
rc = SQLExecDirect(conn->hstmt, (SQLCHAR *) sql, SQL_NTS);
#endif

if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
return 1;
Expand Down Expand Up @@ -2221,38 +2242,63 @@ int msMSSQL2008LayerGetShapeRandom(layerObj *layer, shapeObj *shape, long *recor
shape->numvalues = layer->numitems;

for(t=0; t < layer->numitems; t++) {
/* figure out how big the buffer needs to be */
rc = SQLGetData(layerinfo->conn->hstmt, (SQLUSMALLINT)(t + 1), SQL_C_BINARY, dummyBuffer, 0, &needLen);
if (rc == SQL_ERROR)
handleSQLError(layer);
/* Startwith a 64 character long buffer. This may need to be increased after calling SQLGetData. */
SQLLEN emptyLen = 64;
valueBuffer = (char*) msSmallMalloc(emptyLen);
if ( valueBuffer == NULL ) {
msSetError( MS_QUERYERR, "Could not allocate value buffer.", "msMSSQL2008LayerGetShapeRandom()" );
return MS_FAILURE;
}

if (needLen > 0) {
/* allocate the buffer - this will be a null-terminated string so alloc for the null too */
valueBuffer = (char*) msSmallMalloc( needLen + 2 );
if ( valueBuffer == NULL ) {
msSetError( MS_QUERYERR, "Could not allocate value buffer.", "msMSSQL2008LayerGetShapeRandom()" );
return MS_FAILURE;
}
#ifdef USE_ICONV
SQLSMALLINT targetType = SQL_WCHAR;
#else
SQLSMALLINT targetType = SQL_CHAR;
#endif
SQLLEN totalLen = 0;
char *bufferLocation = valueBuffer;
int r = 0;
while (r < 20) {
rc = SQLGetData(layerinfo->conn->hstmt, (SQLUSMALLINT)(t + 1), targetType, bufferLocation, emptyLen, &retLen);

if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)
totalLen += retLen > emptyLen || retLen == SQL_NO_TOTAL ? emptyLen : retLen;

if (rc == SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA) {
/* We must compensate for the last null termination that SQLGetData include */
/* If we get SQL_NO_TOTAL we do not know how big buffer we need so we increase it with 512. */
#ifdef USE_ICONV
totalLen -= sizeof(wchar_t);
emptyLen = retLen != SQL_NO_TOTAL? retLen - emptyLen + 2 * sizeof(wchar_t): 512;
#else
totalLen -= sizeof(char);
emptyLen = retLen != SQL_NO_TOTAL? retLen - emptyLen + 2 * sizeof(char): 512;
#endif

/* Now grab the data */
rc = SQLGetData(layerinfo->conn->hstmt, (SQLUSMALLINT)(t + 1), SQL_C_BINARY, valueBuffer, needLen, &retLen);
if (rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO)
handleSQLError(layer);
valueBuffer = (char *)msSmallRealloc(valueBuffer, totalLen + emptyLen);
bufferLocation = valueBuffer + totalLen;
} else
break;

r++;
}

/* Terminate the buffer */
valueBuffer[retLen] = 0; /* null terminate it */
if (rc == SQL_ERROR)
handleSQLError(layer);

if (totalLen > 0) {
/* Pop the value into the shape's value array */
#ifdef USE_ICONV
valueBuffer[retLen + 1] = 0;
shape->values[t] = msConvertWideStringToUTF8((wchar_t*)valueBuffer, "UCS-2LE");
msFree(valueBuffer);
#else
shape->values[t] = valueBuffer;
#endif
} else
} else {
/* Copy empty sting for NULL values */
shape->values[t] = msStrdup("");
msFree(valueBuffer);
}
}

/* Get shape geometry */
Expand Down
1 change: 1 addition & 0 deletions mapserver.h
Expand Up @@ -2276,6 +2276,7 @@ void msPopulateTextSymbolForLabelAndString(textSymbolObj *ts, labelObj *l, char
MS_DLL_EXPORT int msHexToInt(char *hex);
MS_DLL_EXPORT char *msGetEncodedString(const char *string, const char *encoding);
MS_DLL_EXPORT char *msConvertWideStringToUTF8 (const wchar_t* string, const char* encoding);
MS_DLL_EXPORT wchar_t *msConvertWideStringFromUTF8 (const char* string, const char* encoding);
MS_DLL_EXPORT int msGetNextGlyph(const char **in_ptr, char *out_string);
MS_DLL_EXPORT int msGetNumGlyphs(const char *in_ptr);
MS_DLL_EXPORT int msGetUnicodeEntity(const char *inptr, unsigned int *unicode);
Expand Down
80 changes: 80 additions & 0 deletions mapstring.cpp
Expand Up @@ -1896,6 +1896,86 @@ char* msConvertWideStringToUTF8 (const wchar_t* string, const char* encoding)
#endif
}

wchar_t* msConvertWideStringFromUTF8 (const char* string, const char* encoding)
{
#ifdef USE_ICONV
wchar_t* output = NULL;
const char* errormessage = NULL;
iconv_t cd = NULL;
size_t nStr;
size_t nInSize;
size_t nOutSize;
size_t iconv_status = -1;
size_t nBufferSize;

const char* pszUTF8 = NULL;
wchar_t* pwszWide = NULL;

if (string != NULL) {
nStr = strlen (string);
nBufferSize = ((nStr * 6) + 1);
output = (wchar_t*) msSmallMalloc (nBufferSize);

if (nStr == 0) {
/* return an empty 8 byte string */
output[0] = '\0';
return output;
}

cd = iconv_open(encoding, "UTF-8");

nOutSize = nBufferSize;
if ((iconv_t)-1 != cd) {
nInSize = sizeof (char)*nStr;
pszUTF8 = string;
pwszWide = output;
iconv_status = msIconv(cd, (char **)&pszUTF8, &nInSize, (char **)&pwszWide, &nOutSize);
if ((size_t)-1 == iconv_status) {
switch (errno) {
case E2BIG:
errormessage = "There is not sufficient room in buffer";
break;
case EILSEQ:
errormessage = "An invalid multibyte sequence has been encountered in the input";
break;
case EINVAL:
errormessage = "An incomplete multibyte sequence has been encountered in the input";
break;
default:
errormessage = "Unknown";
break;
}
msSetError(MS_MISCERR, "Unable to convert string in UTF8 to encoding '%s' %s",
"msConvertWideStringFromUTF8()",
encoding, errormessage);
iconv_close(cd);
msFree(output);
return NULL;
}
iconv_close(cd);
} else {
msSetError(MS_MISCERR, "Encoding not supported by libiconv (%s).",
"msConvertWideStringFromUTF8()",
encoding);
msFree(output);
return NULL;
}
} else {
/* we were given a NULL wide string, nothing we can do here */
return NULL;
}

/* NULL-terminate the output string */
if (nOutSize >= sizeof (wchar_t))
*((wchar_t *) pwszWide) = L'\0';

return output;
#else
msSetError(MS_MISCERR, "Not implemented since Iconv is not enabled.", "msConvertWideStringFromUTF8()");
return NULL;
#endif
}

/*
** Returns the next glyph in string and advances *in_ptr to the next
** character.
Expand Down

0 comments on commit d91fec4

Please sign in to comment.