msdasql: Implement IColumnsInfo GetColumnInfo.

Signed-off-by: Alistair Leslie-Hughes <leslie_alistair@hotmail.com>
This commit is contained in:
Alistair Leslie-Hughes 2021-11-11 13:38:12 +11:00 committed by Alexandre Julliard
parent 98f91b30ed
commit d47201918c
2 changed files with 308 additions and 5 deletions

View file

@ -129,6 +129,154 @@ static struct msdasql_prop msdasql_init_props[] =
{ DBPROP_BLOCKINGSTORAGEOBJECTS, DBPROPFLAGS_DATASOURCEINFO | DBPROPFLAGS_READ | DBPROPFLAGS_WRITE, VT_BOOL, VARIANT_FALSE },
};
#define SQLTYPE_TO_STR(x) case x: return #x
static const char *debugstr_sqltype(SQLSMALLINT type)
{
switch (type)
{
SQLTYPE_TO_STR(SQL_DECIMAL);
SQLTYPE_TO_STR(SQL_CHAR);
SQLTYPE_TO_STR(SQL_VARCHAR);
SQLTYPE_TO_STR(SQL_LONGVARCHAR);
SQLTYPE_TO_STR(SQL_NUMERIC);
SQLTYPE_TO_STR(SQL_GUID);
SQLTYPE_TO_STR(SQL_TINYINT);
SQLTYPE_TO_STR(SQL_SMALLINT);
SQLTYPE_TO_STR(SQL_INTEGER);
SQLTYPE_TO_STR(SQL_REAL);
SQLTYPE_TO_STR(SQL_FLOAT);
SQLTYPE_TO_STR(SQL_DOUBLE);
SQLTYPE_TO_STR(SQL_BINARY);
SQLTYPE_TO_STR(SQL_VARBINARY);
SQLTYPE_TO_STR(SQL_LONGVARBINARY);
SQLTYPE_TO_STR(SQL_TYPE_DATE);
SQLTYPE_TO_STR(SQL_DATE);
SQLTYPE_TO_STR(SQL_TIME);
SQLTYPE_TO_STR(SQL_TYPE_TIMESTAMP);
SQLTYPE_TO_STR(SQL_TIMESTAMP);
SQLTYPE_TO_STR(SQL_TYPE_TIME);
SQLTYPE_TO_STR(SQL_BIGINT);
SQLTYPE_TO_STR(SQL_C_SBIGINT);
SQLTYPE_TO_STR(SQL_C_SLONG);
SQLTYPE_TO_STR(SQL_C_ULONG);
SQLTYPE_TO_STR(SQL_WLONGVARCHAR);
SQLTYPE_TO_STR(SQL_WCHAR);
SQLTYPE_TO_STR(SQL_WVARCHAR);
default:
return "Unknown";
}
}
static const char *debugstr_dbtype(DBTYPE type)
{
switch(type)
{
SQLTYPE_TO_STR(DBTYPE_NUMERIC);
SQLTYPE_TO_STR(DBTYPE_STR);
SQLTYPE_TO_STR(DBTYPE_GUID);
SQLTYPE_TO_STR(DBTYPE_I1);
SQLTYPE_TO_STR(DBTYPE_I2);
SQLTYPE_TO_STR(DBTYPE_UI2);
SQLTYPE_TO_STR(DBTYPE_I4);
SQLTYPE_TO_STR(DBTYPE_I8);
SQLTYPE_TO_STR(DBTYPE_UI4);
SQLTYPE_TO_STR(DBTYPE_R4);
SQLTYPE_TO_STR(DBTYPE_R8);
SQLTYPE_TO_STR(DBTYPE_BYTES);
SQLTYPE_TO_STR(DBTYPE_DBDATE);
SQLTYPE_TO_STR(DBTYPE_DBTIME);
SQLTYPE_TO_STR(DBTYPE_DATE);
SQLTYPE_TO_STR(DBTYPE_DBTIMESTAMP);
SQLTYPE_TO_STR(DBTYPE_WSTR);
default:
return "Unknown";
}
}
static SQLSMALLINT sqltype_to_bindtype(SQLSMALLINT type, BOOL sign)
{
switch (type)
{
case SQL_DECIMAL:
return DBTYPE_NUMERIC;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
case SQL_NUMERIC:
return DBTYPE_STR;
case SQL_GUID:
return DBTYPE_GUID;
case SQL_TINYINT:
return DBTYPE_I1;
case SQL_SMALLINT:
return sign ? DBTYPE_I2 : DBTYPE_UI2;
case SQL_INTEGER:
return sign ? DBTYPE_I4 : DBTYPE_UI4;
case SQL_REAL:
return DBTYPE_R4;
case SQL_FLOAT:
case SQL_DOUBLE:
return DBTYPE_R8;
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
return DBTYPE_BYTES;
case SQL_TYPE_DATE:
return DBTYPE_DBDATE;
case SQL_DATE:
return DBTYPE_DBTIME;
case SQL_TIME:
return DBTYPE_DATE;
case SQL_TYPE_TIMESTAMP:
case SQL_TIMESTAMP:
return DBTYPE_DBTIMESTAMP;
case SQL_TYPE_TIME:
return DBTYPE_DBTIME;
case SQL_BIGINT:
case SQL_C_SBIGINT:
return DBTYPE_I8;
case SQL_C_SLONG:
return DBTYPE_I4;
case SQL_C_ULONG:
return DBTYPE_UI4;
case SQL_WLONGVARCHAR:
case SQL_WCHAR:
case SQL_WVARCHAR:
return DBTYPE_WSTR;
default:
FIXME("Unsupported type %i\n", type);
}
return DBTYPE_I4;
}
static BOOL is_variable_length(SQLSMALLINT type)
{
switch(type)
{
case SQL_LONGVARCHAR:
case SQL_WLONGVARCHAR:
case SQL_LONGVARBINARY:
return TRUE;
}
return FALSE;
}
static BOOL is_fixed_legnth(SQLSMALLINT type)
{
switch(type)
{
case SQL_LONGVARCHAR:
case SQL_WLONGVARCHAR:
case SQL_WVARCHAR:
case SQL_LONGVARBINARY:
case SQL_VARBINARY:
return FALSE;
}
return TRUE;
}
struct msdasql_session
{
IUnknown session_iface;
@ -816,9 +964,134 @@ static ULONG WINAPI rowset_colsinfo_Release(IColumnsInfo *iface)
static HRESULT WINAPI rowset_colsinfo_GetColumnInfo(IColumnsInfo *iface, DBORDINAL *columns,
DBCOLUMNINFO **colinfo, OLECHAR **stringsbuffer)
{
#define MAX_COLUMN_LEN 128
struct msdasql_rowset *rowset = rowset_impl_from_IColumnsInfo( iface );
FIXME("%p, %p, %p, %p\n", rowset, columns, colinfo, stringsbuffer);
return E_NOTIMPL;
DBCOLUMNINFO *dbcolumn;
RETCODE ret;
SQLSMALLINT colcnt;
int i;
OLECHAR *ptr;
TRACE("%p, %p, %p, %p\n", rowset, columns, colinfo, stringsbuffer);
if (!columns || !colinfo || !stringsbuffer)
return E_INVALIDARG;
SQLNumResultCols(rowset->hstmt, &colcnt);
TRACE("SQLNumResultCols %d\n", colcnt);
*columns = colcnt;
ptr = *stringsbuffer = CoTaskMemAlloc(colcnt * MAX_COLUMN_LEN * sizeof(WCHAR));
if (!ptr)
return E_OUTOFMEMORY;
dbcolumn = CoTaskMemAlloc(colcnt * sizeof(DBCOLUMNINFO));
if (!dbcolumn)
{
CoTaskMemFree(ptr);
return E_OUTOFMEMORY;
}
for (i = 0; i < colcnt; i++)
{
SQLWCHAR columnname[MAX_COLUMN_LEN];
SQLSMALLINT ColumnNameLen;
SQLSMALLINT ColumnDataType;
SQLULEN ColumnDataSize;
SQLSMALLINT ColumnDataDigits;
SQLSMALLINT ColumnDataNullable;
ret = SQLDescribeColW(rowset->hstmt, i+1, columnname, MAX_COLUMN_LEN, &ColumnNameLen, &ColumnDataType,
&ColumnDataSize, &ColumnDataDigits, &ColumnDataNullable);
if (SQL_SUCCEEDED(ret))
{
SQLLEN length;
TRACE("%d: Column Name : %s, Column Name Len : %i, SQL Data Type : %i, Data Size : %i, DecimalDigits : %i, Nullable %i\n",
i, debugstr_w(columnname), (int)ColumnNameLen, (int)ColumnDataType, (int)ColumnDataSize, (int)ColumnDataDigits,
(int)ColumnDataNullable);
lstrcpyW(ptr, columnname);
dbcolumn[i].pwszName = ptr;
dbcolumn[i].pTypeInfo = NULL;
dbcolumn[i].iOrdinal = i+1;
ret = SQLColAttribute(rowset->hstmt, i+1, SQL_DESC_UNSIGNED, NULL, 0, NULL, &length);
if (!SQL_SUCCEEDED(ret))
{
CoTaskMemFree(ptr);
CoTaskMemFree(dbcolumn);
ERR("Failed to get column %d attribute\n", i+1);
return E_FAIL;
}
dbcolumn[i].wType = sqltype_to_bindtype(ColumnDataType, length == SQL_FALSE);
TRACE("SQLType %s -> %s\n", debugstr_sqltype(ColumnDataType), debugstr_dbtype(dbcolumn[i].wType));
dbcolumn[i].dwFlags = DBCOLUMNFLAGS_WRITE;
ret = SQLColAttribute(rowset->hstmt, i+1, SQL_DESC_LENGTH, NULL, 0, NULL, &length);
if (!SQL_SUCCEEDED(ret))
{
CoTaskMemFree(ptr);
CoTaskMemFree(dbcolumn);
ERR("Failed to get column %d length (%d)\n", i+1, ret);
return E_FAIL;
}
dbcolumn[i].ulColumnSize = length;
if (dbcolumn[i].ulColumnSize > 1024 && is_variable_length(ColumnDataType))
dbcolumn[i].dwFlags |= DBCOLUMNFLAGS_MAYDEFER | DBCOLUMNFLAGS_ISLONG;
if (ColumnDataNullable)
dbcolumn[i].dwFlags |= DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL;
if (is_fixed_legnth(ColumnDataType))
dbcolumn[i].dwFlags |= DBCOLUMNFLAGS_ISFIXEDLENGTH;
ret = SQLColAttribute(rowset->hstmt, i+1, SQL_DESC_SCALE, NULL, 0, NULL, &length);
if (!SQL_SUCCEEDED(ret))
{
CoTaskMemFree(ptr);
CoTaskMemFree(dbcolumn);
ERR("Failed to get column %d scale (%d)\n", i+1, ret);
return E_FAIL;
}
if (length == 0)
length = 255;
dbcolumn[i].bScale = length;
ret = SQLColAttribute(rowset->hstmt, i+1, SQL_DESC_PRECISION, NULL, 0, NULL, &length);
if (!SQL_SUCCEEDED(ret))
{
CoTaskMemFree(ptr);
CoTaskMemFree(dbcolumn);
ERR("Failed to get column %d precision (%d)\n", i+1, ret);
return E_FAIL;
}
if (length == 0)
length = 255;
dbcolumn[i].bPrecision= length;
dbcolumn[i].columnid.eKind = DBKIND_NAME;
dbcolumn[i].columnid.uName.pwszName = ptr;
ptr += ColumnNameLen + 1;
}
else
{
CoTaskMemFree(ptr);
CoTaskMemFree(dbcolumn);
ERR("Failed to get column %d description (%d)\n", i+1, ret);
return E_FAIL;
}
}
*colinfo = dbcolumn;
#undef MAX_COLUMN_LEN
return S_OK;
}
static HRESULT WINAPI rowset_colsinfo_MapColumnIDs(IColumnsInfo *iface, DBORDINAL column_ids,

View file

@ -517,7 +517,7 @@ static void test_command_rowset(IUnknown *cmd)
hr = ICommandPrepare_Prepare(commandprepare, 1);
ok(hr == DB_E_NOCOMMAND, "got 0x%08lx\n", hr);
hr = ICommandText_SetCommandText(command_text, &DBGUID_DEFAULT, L"CREATE TABLE testing (col1 INT, col2 SHORT)");
hr = ICommandText_SetCommandText(command_text, &DBGUID_DEFAULT, L"CREATE TABLE testing (col1 INT, col2 VARCHAR(20) NOT NULL, col3 FLOAT)");
ok(hr == S_OK, "got 0x%08lx\n", hr);
hr = ICommandPrepare_Prepare(commandprepare, 1);
@ -532,7 +532,7 @@ static void test_command_rowset(IUnknown *cmd)
if (unk)
IUnknown_Release(unk);
hr = ICommandText_SetCommandText(command_text, &DBGUID_DEFAULT, L"insert into testing values(1, 0)");
hr = ICommandText_SetCommandText(command_text, &DBGUID_DEFAULT, L"insert into testing values(1, 'red', 1.0)");
ok(hr == S_OK, "got 0x%08lx\n", hr);
affected = 9999;
@ -550,13 +550,43 @@ static void test_command_rowset(IUnknown *cmd)
ok(unk != NULL, "Unexpected value\n");
if (hr == S_OK)
{
ok(affected == -1, "got %Id\n", affected);
const DWORD flag1 = DBCOLUMNFLAGS_ISFIXEDLENGTH | DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL | DBCOLUMNFLAGS_WRITE;
const DWORD flag2 = DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYBENULL | DBCOLUMNFLAGS_WRITE;
IColumnsInfo *colinfo;
DBORDINAL columns;
DBCOLUMNINFO *dbcolinfo;
OLECHAR *stringsbuffer;
todo_wine ok(affected == -1, "got %Id\n", affected);
hr = IUnknown_QueryInterface(unk, &IID_IRowset, (void**)&rowset);
ok(hr == S_OK, "got 0x%08lx\n", hr);
test_rowset_interfaces(rowset, command_text);
hr = IRowset_QueryInterface(rowset, &IID_IColumnsInfo, (void**)&colinfo);
ok(hr == S_OK, "got 0x%08lx\n", hr);
columns = 0;
hr = IColumnsInfo_GetColumnInfo(colinfo, &columns, &dbcolinfo, &stringsbuffer);
ok(hr == S_OK, "got 0x%08lx\n", hr);
ok(columns == 3, "got %Iu\n", columns);
ok(dbcolinfo[0].dwFlags == flag1, "got 0x%08lx\n", dbcolinfo[0].dwFlags);
ok(dbcolinfo[0].wType == DBTYPE_I4, "got 0x%08x\n", dbcolinfo[0].wType);
todo_wine ok(dbcolinfo[1].dwFlags == flag2, "got 0x%08lx\n", dbcolinfo[1].dwFlags);
ok(dbcolinfo[1].wType == DBTYPE_WSTR /* Unicode MySQL Driver */ ||
dbcolinfo[1].wType == DBTYPE_STR /* ASCII MySQL Driver */, "got 0x%08x\n", dbcolinfo[1].wType);
ok(dbcolinfo[2].dwFlags == flag1, "got 0x%08lx\n", dbcolinfo[2].dwFlags);
ok(dbcolinfo[2].wType == DBTYPE_R4 /* MySQL */ ||
dbcolinfo[2].wType == DBTYPE_R8 /* Access */, "got 0x%08x\n", dbcolinfo[2].wType);
CoTaskMemFree(dbcolinfo);
CoTaskMemFree(stringsbuffer);
IColumnsInfo_Release(colinfo);
IRowset_Release(rowset);
IUnknown_Release(unk);
}