Reputation: 13
Have set up Always Encrypted for my table columns at server side. From a C++ client, I use the below connection string to connect to the database:
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=;";
From the same client I invoke below command to insert data:
CString csQStrInsert = L"declare @val1 int = 3; declare @val2 int = 3; insert into [dbo].[Table_AlwaysEncrypted] ([col1], [col2]) values (@val1, @val2);";
pDatabase->ExecuteSQL(csQStrInsert);
Unfortunately the query fails with below error:
pEX->m_strError = L"Encryption scheme mismatch for columns/variables '@val1'. The encryption scheme for the columns/variables is (encryption_type = 'PLAINTEXT') and the expression near line '1' expects it to be DETERMINISTIC, or PLAIN TEXT.
What am I doing wrong?
Upvotes: 0
Views: 866
Reputation: 71144
You cannot use local variables for Always Encrypted columns, they must come from client-side parameters. In SSMS it works because SSMS parses your script and pulls out the variables into parameters, but in C++ or other clients you must parameterize it yourself.
For example, the below code is used as an example on Microsoft's website, see there for more info on how to use Always Encrypted:
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);
As you can see, SQLBindParameter
is used to add parameters to the query. You cannot use either literals or SQL local variables to insert or compare with encrypted columns, as the server has no access to the decrypted data.
The client driver needs to have access to the relevant certificate.
Upvotes: 2