Always Encrypted - Parameters - sql-server

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?

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.

Related

How to pass Sqlite query values as variables?

My code is like this...
char dis[20];
int tc,tac,trc;
puts("Enter the data:\n");
puts("District : ");
while((getchar())!='\n');
fgets(dis,20,stdin);
puts("Total Cases : ");
scanf("%d",&tc);
puts("Total Active Cases : ");
scanf("%d",&tac);
puts("Total Recovered Cases : ");
scanf("%d",&trc);
sql = "INSERT INTO COV VALUES (dis,tc,tac,trc);"; //won't work
sql = "INSERT INTO COV VALUES ('abc',1,1,0);"; //works
database = sqlite3_exec(db, sql,0,0,0);
I want to save the values obtained from user in sqlite database but I can't do it as shown below.
It works if I just pass the exact value (i.e. during compile time).
How can I send values computed during runtime execution to sqlite database?
Have a look to the Sqlite C interface documentation.
Assuming you have a table defined like this:
CREATE TABLE COV (id PRIMARY KEY, dis VARCHAR, tc INTEGER, tac INTEGER, trc INTEGER);
You need to bind your parameters with specific bind API to prevent SQL injection.
Prepare your INSERT string using ?N template:
char sql[512];
snprintf(sql, sizeof(sql), "INSERT INTO COV(dis, tc, tac, trc) VALUES (?1,?2,?3,?4);");
Then bind your program variables with the corresponding parameter:
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, sql, sizeof(sql), &stmt, NULL);
sqlite3_bind_text(stmt, 1, dis, 20, NULL);
sqlite3_bind_int(stmt, 2, tc);
sqlite3_bind_int(stmt, 3, tac);
sqlite3_bind_int(stmt, 4, trc);
ret = sqlite3_step(stmt);
if (ret == SQLITE_DONE)
printf("record inserted!\n");
else
printf("Error: %s\n", sqlite3_errmsg(db));
sqlite3_finalize(stmt);
You need to put the actual values in using sprintf
char sqlscript[128];
Then, since you are using sql to send to the DB, assign sqlscript to sql first
sql = sqlscript;
sprintf(sql, "INSERT INTO COV VALUES('%s', %d, %d, %d);", dis, tc, tac, trc);

Inserting values using Application Engine

I am trying to insert the values from my text file to a record using app engine.
I know this could be done using file layout but my requirement suggests not to use file layout but needs to insert into record using app engine people code.
I am writing the following PeopleCode in my appengine
(here i am trying to add only one field value to my record in this example)
Local File &MYFILE;
Local Record &REC_ERN;
&MYFILE = GetFile("c:\sample.csv", "R", %FilePath_Absolute);
If &MYFILE.IsOpen Then
While &MYFILE.ReadLine(&STRING);
MessageBox(0, "", 0, 0, &STRING);
&source_type_id = Substring(&STRING, 1, 3);
&Stmt= "INSERT INTO KGS_TEST (ID) VALUES ( &source_type_id)";
SQLExec(&Stmt);
REM MessageBox(0, "", 0, 0, &source_type_id);
End-While;
End-If;
&MYFILE.Close();
The problem I am facing is &source_type_id = Substring(&STRING, 1, 3);
the variable &source_type_id has the value but i need to insert this into a record which I have created which has fields (ID,NAME,AGE,department).
The issue you are having is that your variable &source_type_id is inside a string and therefore is interpreted literally as part of the string instead of the value of the variable as you want.
What you need to do is put a bind variable in the string for the value and then pass the value as a parameter to SqlExec.
Local File &MYFILE;
&MYFILE = GetFile("c:\sample.csv", "R", %FilePath_Absolute);
If &MYFILE.IsOpen Then
While &MYFILE.ReadLine(&STRING);
MessageBox(0, "", 0, 0, &STRING);
&source_type_id = Substring(&STRING, 1, 3);
&Stmt= "INSERT INTO KGS_TEST (ID) VALUES ( :1)";
SQLExec(&Stmt,&source_type_id);
REM MessageBox(0, "", 0, 0, &source_type_id);
End-While;
End-If;
&MYFILE.Close();

Output Parameter of Stored Procedure is not getting set

So I have the following stored procedure:
CREATE PROCEDURE dbo.uspUpdateOrInsertDataObjectValue
#ParentDataObjectId INT,
#DataObjectName NVARCHAR(256),
#DataObjectValue NVARCHAR(256),
#ForceInsert BIT,
#ReturnDataObjectId INT OUT
WITH RECOMPILE
AS
BEGIN
if #ForceInsert = 0
BEGIN
UPDATE DataObjects SET Value=#DataObjectValue WHERE Name=#DataObjectName AND ParentDataObjectId=#ParentDataObjectId
END
if ( #ForceInsert = 1 ) OR ( ##ROWCOUNT = 0 )
BEGIN
--Spend time on the hierarchy id stuff only if we have to insert
DECLARE #ParentTreeId HierarchyId
DECLARE #LastChildTreeId HierarchyId
SELECT #ParentTreeId = TreeId FROM DataObjects WHERE DataObjectId=#ParentDataObjectId
SELECT #LastChildTreeId = MAX(TreeId) FROM DataObjects WHERE TreeId.GetAncestor(1)=#ParentTreeId
INSERT INTO DataObjects ( Name, Value, ParentDataObjectId, TreeId ) VALUES ( #DataObjectName, #DataObjectValue, #ParentDataObjectId, #ParentTreeId.GetDescendant( #LastChildTreeId, NULL ) )
END
SELECT #ReturnDataObjectId = (SELECT TOP 1 DataObjectId FROM DataObjects WHERE Name=#DataObjectName AND ParentDataObjectId=#ParentDataObjectId ORDER BY DataObjectId DESC)
RETURN 0
END
GO
`
And I'm executing it like this:
int iParentDataObjectId = 1;
int iDataObjectId = 0;
SQLWCHAR name[256];
SQLWCHAR value[256];
_sntprintf_s( (wchar_t*)name, 256, _TRUNCATE, _T("%s"), pCDBDataObject->GetName() );
_sntprintf_s( (wchar_t*)value, 256, _TRUNCATE, _T("%s"), pCDBDataObject->GetString() );
result = SQLBindParameter( sqlStatementHandle, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &iParentDataObjectId, 0, NULL );
result = SQLBindParameter( sqlStatementHandle, 2, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, 256, 0, name, 256, NULL );
result = SQLBindParameter( sqlStatementHandle, 3, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, 256, 0, value, 256, NULL );
result = SQLBindParameter( sqlStatementHandle, 4, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, 1, 0, &forceInsert, 0, NULL );
result = SQLBindParameter( sqlStatementHandle, 5, SQL_PARAM_OUTPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &iDataObjectId, 0, NULL );
result = SQLExecDirect( sqlStatementHandle, (SQLWCHAR*)_T("{CALL uspUpdateOrInsertDataObjectValue (?,?,?,?,?)}"), SQL_NTS );
'
The stored procedure executes properly and inserts or updates the database but iDataObjectId never changes.
However when I execute it like this in a script in Sql Server Management Studio:
DECLARE #testval int
EXECUTE uspUpdateOrInsertDataObjectValue 1, 'Config', 'testasdfasdfasdfas', 1, #testval OUT
SELECT * from DataObjects where DataObjectId=#testval
the output value is assigned to #testval and the subsequent select statement works just fine.
What am I missing?
I found the answer after a more careful examination of this example: http://technet.microsoft.com/en-us/library/ms403283.aspx
You need to clean out the result sets of the query(call) with SqlMoreResults before the output variables get set. Not the behavior I would have expected but it was right there in the documentation.

EXEC %%System().FireTrigger

I was just wondering what is this?
EXEC %%System().FireTrigger(ID = 225, ID = 102, ID = 0, ID = 0, Value = #server,
ID = -1, ID = 0, ID = 0, Value = NULL,
ID = 7, Value = #server, Value = #srvproduct2, Value = #provider, Value = #datasrc, Value = #location, Value = NULL, Value = #catalog)
I have been trying to look for it in mssql but I cant find it, what does it mean? I know EXEC is used to execute stored procedure, but what is %%System()?
and what is FireTrigger? is it an embedded command in the system? external dll? been trying to Google it but it seems to be vague at the moment.
This is an internal mechanism that is sometimes exposed but you can't call it yourself and it is undocumented.

OLEDB: What is the fastest way to get column metadata for a SQL Server CE table, without selecting any rows?

I have to use OLE DB with an SQL Server CE. My task is to obtain the metadata on all the columns in a table.
One way to do it is select all the fields of any row and then obtain IColumnInfo from the resulting row set. However, this is done at the cost of selecting a row.
My question - is it the fastest way or is there a better way to get hold on the DBCOLUMNINFO objects for all the columns in a table?
You can use a SELECT statement on the INFORMATION_SCHEMA to extract COLUMN information for a particular table:
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = ?
However, in OLEDB there's an IDBSchemaRowset which can be a lot faster. I've put together a small C++ code sample that shows opening an OLEDB connection (OpenDatabase) and extracting column information using IRowset (GetSchemaColumns). You need to add processing the results from IRowset returned (Run):
#include <windows.h>
#include <atlbase.h>
#include <oleauto.h>
#include <oledb.h>
HRESULT OpenDatabase(LPOLESTR szPath, IDBInitialize **ppIDBInitialize)
{
HRESULT hr = S_OK;
CComPtr<IDBInitialize> spIDBInitialize;
hr = spIDBInitialize.CoCreateInstance(OLESTR("Microsoft.SQLLITE.MOBILE.OLEDB.3.0"));
CComPtr<IDBProperties> spIDBProperties;
spIDBProperties = spIDBInitialize;
CComVariant vDataSource(szPath);
DBPROP dbProp = { DBPROP_INIT_DATASOURCE, DBPROPOPTIONS_REQUIRED, 0, DB_NULLID, vDataSource };
DBPROPSET dbPropSet = { &dbProp, 1, DBPROPSET_DBINIT };
hr = spIDBProperties->SetProperties(1, &dbPropSet);
hr = spIDBInitialize->Initialize();
*ppIDBInitialize = spIDBInitialize.Detach();
return hr;
}
HRESULT GetSchemaColumns(IUnknown *pDataSource, LPOLESTR pTableName, IRowset **ppIRowset)
{
HRESULT hr = S_OK;
CComPtr<IDBCreateSession> spIDBCreateSession;
hr = pDataSource->QueryInterface(IID_IDBCreateSession, (void**) &spIDBCreateSession);
CComPtr<IDBCreateCommand> spIDBCreateCommand;
hr = spIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**) &spIDBCreateCommand);
CComPtr<IDBSchemaRowset> spIDBSchemaRowset;
hr = spIDBCreateCommand->QueryInterface(IID_IDBSchemaRowset, (void**) &spIDBSchemaRowset);
CComVariant vRestrictions[CRESTRICTIONS_DBSCHEMA_COLUMNS];
vRestrictions[2] = pTableName;
CComPtr<IRowset> spIRowset;
hr = spIDBSchemaRowset->GetRowset(NULL, DBSCHEMA_COLUMNS, CRESTRICTIONS_DBSCHEMA_COLUMNS, vRestrictions, IID_IRowset, NULL, NULL, (IUnknown**) &spIRowset);
*ppIRowset = spIRowset.Detach();
return hr;
}
HRESULT Run()
{
HRESULT hr = S_OK;
CComPtr<IDBInitialize> spIDBInitialize;
hr = OpenDatabase(OLESTR("MyDatabase.sdf"), &spIDBInitialize);
CComPtr<IRowset> spIRowset;
hr = GetSchemaColumns(spIDBInitialize, OLESTR("MyTableName"), &spIRowset);
DBCOUNTITEM cRows = 0;
HROW hRow = NULL;
HROW *phRow = &hRow;
hr = spIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &phRow);
while (SUCCEEDED(hr) && cRows > 0)
{
// Do handling of a row fetched from INFORMATION_SCHEMA.COLUMN here
// ...
hr = spIRowset->ReleaseRows(1, phRow, NULL, NULL, NULL);
hr = spIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRows, &phRow);
}
return hr;
}
If you want to avoid selecting rows, use where 1 = 0 as your where clause.
You could also use the INFORMATION_SCHEMA tables.
Microsoft docs: Information Schema (SQL Server Compact)

Resources