在服务器端为我的表列设置了Always Encrypted。从C++客户端,我使用以下连接字符串连接到数据库:
CString connString = L"Driver={ODBC Driver 17 for SQL Server};Server=192.122.200.200,1433;Encrypt=no;Trusted_Connection=no;ColumnEncryption=Enabled;DATABASE=AlwaysEncrypted;UID=sa;PWD=;";
从同一个客户端,我调用以下命令插入数据:
CString csQStrInsert = L"declare @val1 int = 3; declare @val2 int = 3; insert into [dbo].[Table_AlwaysEncrypted] ([col1], [col2]) values (@val1, @val2);";
pDatabase->ExecuteSQL(csQStrInsert);
不幸的是,查询失败,出现以下错误:
pEX->m_strError=L";列/变量"@val1"的加密方案不匹配。列/变量的加密方案为(encryption\utype="PLAINTEXT"(,第"1"行附近的表达式要求其为DETERMINISTIC或PLAIN TEXT。
我做错了什么?
不能为"始终加密"列使用局部变量,它们必须来自客户端参数。在SSMS中,它是有效的,因为SSMS解析您的脚本并将变量提取为参数,但在C++或其他客户端中,您必须自己对其进行参数化。
例如,下面的代码被用作微软网站上的一个示例,请参阅那里了解有关如何使用始终加密的更多信息:
SQL_DATE_STRUCT date;
SQLLEN cbdate; // size of date structure
SQLCHAR SSN[12];
strcpy_s((char*)SSN, _countof(SSN), "795-73-9838");
SQLWCHAR* firstName = L"Catherine";
SQLWCHAR* lastName = L"Abel";
SQLINTEGER cbSSN = SQL_NTS, cbFirstName = SQL_NTS, cbLastName = SQL_NTS;
// Initialize the date structure
date.day = 10;
date.month = 9;
date.year = 1996;
// Size of structures
cbdate = sizeof(SQL_DATE_STRUCT);
SQLRETURN rc = 0;
string queryText = "INSERT INTO [dbo].[Patients] ([SSN], [FirstName], [LastName], [BirthDate]) VALUES (?, ?, ?, ?) ";
rc = SQLPrepare(hstmt, (SQLCHAR *)queryText.c_str(), SQL_NTS);
//SSN
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 11, 0, (SQLPOINTER)SSN, 0, &cbSSN);
//FirstName
rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, 50, 0, (SQLPOINTER)firstName, 0, &cbFirstName);
//LastName
rc = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, 50, 0, (SQLPOINTER)lastName, 0, &cbLastName);
//BirthDate
rc = SQLBindParameter(hstmt, 4, SQL_PARAM_INPUT, SQL_C_TYPE_DATE, SQL_TYPE_DATE, 10, 0, (SQLPOINTER)&date, 0, &cbdate);
rc = SQLExecute(hstmt);
如您所见,SQLBindParameter
用于向查询添加参数。不能使用文字或SQL本地变量插入加密列或与加密列进行比较,因为服务器无法访问解密的数据。
客户端驱动程序需要具有访问相关证书的权限。