支持架构行集

使用架构行集,使用者可以在不知道数据存储的基础结构或架构的情况下,获取数据存储的相关信息。 例如,数据存储可能有采用用户定义层次结构的表,因此除了读取它之外,没有任何方法可确保了解架构。 (作为另一个示例,Visual C++ 向导使用架构行集为使用者生成访问器。)为了允许使用者执行此操作,提供程序的会话对象在 IDBSchemaRowset 接口上公开这些方法。 在 Visual C++ 应用程序中,使用 IDBSchemaRowsetImpl 类来实现 IDBSchemaRowset

IDBSchemaRowsetImpl 支持以下方法:

ATL OLE DB 提供程序向导支持

ATL OLE DB 提供程序向导不适用于 Visual Studio 2019 及更高版本。

ATL OLE DB 提供程序向导在会话头文件中创建三个架构类:

  • CShortNameSessionTRSchemaRowset

  • CShortNameSessionColSchemaRowset

  • CShortNameSessionPTSchemaRowset

这些类响应使用者对架构信息的请求;请注意,OLE DB 规范要求,必须支持这三个架构行集:

  • CShortNameSessionTRSchemaRowset 处理对表信息的请求(DBSCHEMA_TABLES 架构行集)。

  • CShortNameSessionColSchemaRowset 处理对列信息的请求(DBSCHEMA_COLUMNS 架构行集)。 向导提供了这些类的示例实现,它们返回 DOS 提供程序的架构信息。

  • CShortNameSessionPTSchemaRowset 处理对提供程序类型的相关架构信息的请求(DBSCHEMA_PROVIDER_TYPES 架构行集)。 向导提供的默认实现返回 S_OK。

可以将这些类自定义为处理适合提供程序的架构信息:

  • 在 CShortNameSessionTRSchemaRowset 中,必须填写目录、表和说明字段(trData.m_szTypetrData.m_szTabletrData.m_szDesc)。 向导生成的示例仅使用一行(一个表)。 其他提供程序可能会返回多个表。

  • 在 CShortNameSessionColSchemaRowset 中,将表名称作为 DBID 进行传递。

设置限制

与架构行集支持相关的一个重要概念是,使用 SetRestrictions 设置限制。 限制允许使用者只提取匹配行(例如,查找表“MyTable”中的所有列)。 限制是可选的,如果不支持任何限制(默认设置),则始终返回所有数据。 有关支持限制的提供程序示例,请参阅 UpdatePV 示例。

设置架构映射

设置架构映射(如 UpdatePV 中 Session.h 内的架构映射):

BEGIN_SCHEMA_MAP(CUpdateSession)
    SCHEMA_ENTRY(DBSCHEMA_TABLES, CUpdateSessionTRSchemaRowset)
    SCHEMA_ENTRY(DBSCHEMA_COLUMNS, CUpdateSessionColSchemaRowset)
    SCHEMA_ENTRY(DBSCHEMA_PROVIDER_TYPES, CUpdateSessionPTSchemaRowset)
END_SCHEMA_MAP()

必须支持 DBSCHEMA_TABLES、DBSCHEMA_COLUMNS 和 DBSCHEMA_PROVIDER_TYPES,才能支持 IDBSchemaRowset。 可以自行添加其他架构行集。

使用 Execute 方法声明架构行集类(如 UpdatePV 中的 CUpdateSessionTRSchemaRowset):

class CUpdateSessionTRSchemaRowset :
    public CSchemaRowsetImpl < CUpdateSessionTRSchemaRowset,
                              CTABLESRow, CUpdateSession >
...
// Execute looks like this; what pointers does the consumer use?
    HRESULT Execute(DBROWCOUNT* pcRowsAffected,
                    ULONG cRestrictions, const VARIANT* rgRestrictions)

由于 CUpdateSession 继承自 IDBSchemaRowsetImpl,因此它包含所有限制处理方法。 使用 CSchemaRowsetImpl 时,声明(上面架构映射中列出的)三个子类:CUpdateSessionTRSchemaRowsetCUpdateSessionColSchemaRowsetCUpdateSessionPTSchemaRowset。 其中每个子类都有 Execute 方法,用于处理各自的一组限制(搜索条件)。 每个 Execute 方法都比较 cRestrictions 和 rgRestrictions 参数的值。 有关这些参数的说明,请参阅 SetRestrictions

若要详细了解与特定架构行集对应的限制,请参阅 IDBSchemaRowset 中的架构行集 GUID 表(这篇文章收录在 Windows SDK 的“OLE DB 程序员参考”中)。

例如,如果对 DBSCHEMA_TABLES 支持了 TABLE_NAME 限制,便会执行以下操作:

首先,查找 DBSCHEMA_TABLES,并确定它是否支持以下四个限制(按顺序)。

架构行集限制 限制值
TABLE_CATALOG 0x1(二进制 1)
TABLE_SCHEMA 0x2(二进制 10)
TABLE_NAME 0x4(二进制 100)
TABLE_TYPE 0x8(二进制 1000)

接下来,每个限制有一个对应位。 由于只想要支持 TABLE_NAME,因此会在 rgRestrictions 元素中返回 0x4。 如果支持了 TABLE_CATALOG 和 TABLE_NAME,便会返回 0x5(二进制 101)。

默认情况下,实现对任何请求都返回 0(不支持任何限制)。 例如,UpdatePV 是支持限制的提供程序。

示例

此代码摘自 UpdatePV 示例。 UpdatePv 支持三个必需的架构行集:DBSCHEMA_TABLES、DBSCHEMA_COLUMNS 和 DBSCHEMA_PROVIDER_TYPES。 本主题通过逐步介绍如何实现 DBSCHEMA_TABLE 行集,举例说明了如何在提供程序中实现架构支持。

注意

示例代码可能与本文列出的代码不同;应将示例代码视为最新版本。

添加架构支持的第一步是,确定要支持哪些限制。 若要确定哪些限制可用于架构行集,请查看 OLE DB 规范中的 IDBSchemaRowset 定义。 主定义后面是包含架构行集名称、限制数和限制列的表。 选择要支持的架构行集,并记下限制数和限制列。 例如,DBSCHEMA_TABLES 支持四个限制(TABLE_CATALOG、TABLE_SCHEMA、TABLE_NAME 和 TABLE_TYPE):

void SetRestrictions(ULONG cRestrictions, GUID* rguidSchema,
   ULONG* rgRestrictions)
{
    for (ULONG l=0; l<cRestrictions; l++)
    {
        if (InlineIsEqualGUID(rguidSchema[l], DBSCHEMA_TABLES))
            rgRestrictions[l] = 0x0C;
        else if (InlineIsEqualGUID(rguidSchema[l], DBSCHEMA_COLUMNS))
                 rgRestrictions[l] = 0x04;
             else if (InlineIsEqualGUID(rguidSchema[l],
                                        DBSCHEMA_PROVIDER_TYPES))
                      rgRestrictions[l] = 0x00;
   }
}

位表示每个限制列。 若要支持限制(即可以通过它进行查询),请将相应位设置为 1。 如果不想支持限制,请将相应位设置为 0。 在上面的代码行中,UpdatePV 对 DBSCHEMA_TABLES 行集支持 TABLE_NAME 和 TABLE_TYPE 限制。 这两个分别是第三个(位掩码 100)和第四个(位掩码 1000)限制。 因此,UpdatePv 的位掩码为 1100(或 0x0C):

if (InlineIsEqualGUID(rguidSchema[l], DBSCHEMA_TABLES))
    rgRestrictions[l] = 0x0C;

以下 Execute 函数类似于常规行集中的函数。 有三个参数:pcRowsAffected、cRestrictions 和 rgRestrictions。 pcRowsAffected 变量是提供程序可用来返回架构行集中行数的输出参数。 cRestrictions 参数是包含使用者传递给提供程序的限制数的输入参数。 rgRestrictions 参数是包含限制值的 VARIANT 值数组。

HRESULT Execute(DBROWCOUNT* pcRowsAffected, ULONG cRestrictions,
                const VARIANT* rgRestrictions)

cRestrictions 变量基于架构行集的限制总数,无论提供程序是否支持它们。 因为 UpdatePv 支持两个限制(第三个和第四个),所以此代码仅查找大于或等于 3 的 cRestrictions 值。

TABLE_NAME 限制值存储在 rgRestrictions [2] 中(同样,从零开始的数组中的第三个限制为 2)。 检查限制是否不是 VT_EMPTY,以真正支持它。 请注意,VT_NULL 不等于 VT_EMPTY。 VT_NULL 指定有效限制值。

表名称的 UpdatePv 定义是文本文件的完全限定路径名称。 提取限制值,然后尝试打开文件,以确保文件确实存在。 如果文件不存在,返回的是 S_OK。 这看起来可能有点倒退,但代码实际上是在指示使用者,根据指定的名称找不到任何受支持的表。 返回 S_OK 表示代码已正确执行。

USES_CONVERSION;
enum {
            sizeOfszFile = 255
};
CTABLESRow  trData;
FILE        *pFile = NULL;
TCHAR       szFile[ sizeOfszFile ];
errcode     err = 0;

// Handle any restrictions sent to us. This only handles
// the TABLE_NAME & TABLE_TYPE restictions (the 3rd and 4th
// restrictions in DBSCHEMA_TABLES...look in IDBSchemaRowsets
// in part 2 of the prog. ref) so your restrictions are 0x08 & 0x04
// for a total of (0x0C)
if (cRestrictions >= 3 && rgRestrictions[2].vt != VT_EMPTY)
{
    CComBSTR bstrName = rgRestrictions[2].bstrVal;
    if ((rgRestrictions[2].vt == VT_BSTR) && (bstrName != (BSTR)NULL))
    {
        // Check to see if the file exists
        _tcscpy_s(&szFile[0], sizeOfszFile, OLE2T(bstrName));
        if (szFile[0] == _T('\0') ||
           ((err = _tfopen(&pFile, &szFile[0], _T("r"))) == 0))
        {
            return S_OK;// Their restriction was invalid return no data
        }
        else
        {
            fclose(pFile);
        }
    }
}

支持的第四个限制 (TABLE_TYPE) 类似于第三个限制。 检查值是否不是 VT_EMPTY。 此限制仅返回表类型 TABLE。 若要确定 DBSCHEMA_TABLES 的有效值,请查看“OLE DB 程序员参考”的“附录 B”中的 TABLES 行集部分。

// TABLE_TYPE restriction:
if (cRestrictions >=4 && rgRestrictions[3].vt != VT_EMPTY)
{
    CComBSTR bstrType = rgRestrictions[3].bstrVal;
    if ((rgRestrictions[3].vt == VT_BSTR) && (bstrType != (BSTR)NULL))
    {
        // This is kind of a blind restriction.
        // This only actually supports
        // TABLES so if you get anything else,
        // just return an empty rowset.
        if (_tcscmp(_T("TABLE"), OLE2T(bstrType)) != 0)
            return S_OK;
    }
}

这是为行集实际创建行条目的位置。 变量 trData 对应于 CTABLESRow,这是 OLE DB 提供程序模板中定义的结构。 CTABLESRow 对应于 OLE DB 规范的“附录 B”中的 TABLES 行集定义。 只需要添加一行,因为一次只能支持一个表。

// Bring over the data:
wcspy_s(trData.m_szType, OLESTR("TABLE"), 5);

wcspy_s(trData.m_szDesc, OLESTR("The Directory Table"), 19);

wcsncpy_s(trData.m_szTable, T2OLE(szFile), _TRUNCATE());

UpdatePV 仅设置三列:TABLE_NAME、TABLE_TYPE 和 DESCRIPTION。 记下返回其信息的列,因为在实现 GetDBStatus 时需要用到此类信息:

    _ATLTRY
    {
        m_rgRowData.Add(trData);
    }
    _ATLCATCHALL()
    {
        return E_OUTOFMEMORY;
    }
    //if (!m_rgRowData.Add(trData))
    //    return E_OUTOFMEMORY;
    *pcRowsAffected = 1;
    return S_OK;
}

GetDBStatus 函数对架构行集执行正确操作非常重要。 因为不返回 TABLES 行集中每个列的数据,所以需要指定返回和不返回哪些列的数据。

virtual DBSTATUS GetDBStatus(CSimpleRow* , ATLCOLUMNINFO* pColInfo)
{
    ATLASSERT(pColInfo != NULL);

    switch(pColInfo->iOrdinal)
    {
    case 3:     // TABLE_NAME
    case 4:     // TABLE_TYPE
    case 6:     // DESCRIPTION
        return DBSTATUS_S_OK;
        break;
    default:
        return DBSTATUS_S_ISNULL;
    break;
    }
}

因为 Execute 函数从 TABLES 行集中返回 TABLE_NAME、TABLE_TYPE 和 DESCRIPTION 字段的数据,所以你可以查看 OLE DB 规范的“附录 B”,并确定(通过自上而下计数)它们是序号 3、4 和 6。 对于其中每一列,返回的是 DBSTATUS_S_OK。 对于其他所有列,返回的是 DBSTATUS_S_ISNULL。 请务必返回此状态,因为使用者可能不知道返回值是 NULL 或其他值。 同样,请注意,NULL 不相当于空。

若要详细了解 OLE DB 架构行集接口,请参阅“OLE DB 程序员参考”中的 IDBSchemaRowset 接口。

若要了解使用者如何使用 IDBSchemaRowset 方法,请参阅使用架构行集获取元数据

有关支持架构行集的提供程序示例,请参阅 UpdatePV 示例。

另请参阅

高级提供程序技术