Something I didn’t understand at all when I started writing this series was how the “None” data privacy level worked. Now, however, the ever- helpful Curt Hagenlocher of the Power Query dev team has explained it to me and in this post I’ll demonstrate how it behaves and show how data privacy levels can be inherited from other data sources.
Let’s go back to the original example I used in part 1 of this series where I showed how data from an Excel workbook can be combined with data from SQL Server, and how the data privacy settings on each data source determine whether query folding takes place or not (I suggest you read that post before continuing to get some background). Now, imagine that the Excel workbook is in a folder called C:\Data Privacy Demo, and a query called FilterDay is used to get data from it:
let Source = Excel.Workbook( File.Contents( "C:\Data Privacy Demo\FilterParameter.xlsx" ) , null, true), FilterDay_Table = Source{[Item="FilterDay",Kind="Table"]}[Data], ChangedType = Table.TransformColumnTypes( FilterDay_Table, {{"Parameter", type text}} ), Output = ChangedType{0}[#"Parameter"] in Output
This query gets the name of a weekday from a table in the workbook, for example the text “Friday”:
When this query is referenced in a second query that uses the day name to filter the data in a table in SQL Server, like so:
let Source = Sql.Databases("localhost"), DB = Source{[Name="Adventure Works DW"]}[Data], dbo_DimDate = DB{[Schema="dbo",Item="DimDate"]}[Data], RemovedColumns = Table.SelectColumns(dbo_DimDate, {"DateKey", "EnglishDayNameOfWeek"}), FilteredRows = Table.SelectRows(RemovedColumns, each ([EnglishDayNameOfWeek] = FilterDay)) in FilteredRows
…and the query is run for the first time, then you will get prompted for credentials to access SQL Server and after that you’ll get prompted to set data privacy levels on both data sources used:
The dropdown boxes in the second column allow you to set the data privacy settings for each data source, but look at the data sources listed in the first column. There are two things to point out:
- The data sources the two queries are accessing are the DimDate table in the Adventure Works DW database on localhost, and the file C:\Data Privacy Demo\FilterParameter.xlsx. However you’re not being prompted to set data privacy levels on those exact data sources, you’re being prompted to set data privacy levels on the localhost instance and the c:\ drive
- The data source names are displayed in dropdown boxes, so there are other options to select here
Clicking each dropdown box is revealing:
For the SQL Server database you can set the data privacy level at two places: the localhost instance (the default), or the Adventure Works DW database on that instance. For the Excel workbook you get set the data privacy level at three places: the c:\ drive (the default), the folder c:\Data Privacy Demo that the Excel workbook is in, or the Excel workbook itself.
Let’s say you accept the defaults and set the data privacy settings to Public on localhost and the c:\ drive:
As you would expect after reading part 1 of this series, the query runs and query folding takes place:
Now, let’s say you copy the Excel file up to the root of the c:\ drive and rename it to filterparameter2.xlsx, then update the FilterDay query above to load data from this new Excel file instead:
let Source = Excel.Workbook( File.Contents( "C:\FilterParameter2.xlsx" ) , null, true), FilterDay_Table = Source{[Item="FilterDay",Kind="Table"]}[Data], ChangedType = Table.TransformColumnTypes( FilterDay_Table, {{"Parameter", type text}} ), Output = ChangedType{0}[#"Parameter"] in Output
At this point, when you click the Data Source Settings button and look at the permissions for the file c:\filterparameter2.xlsx you will see that the privacy level is set to None:
However, it behaves as if it has a data privacy level of Public: the second query that gets data from SQL Server runs successfully, query folding still takes place and you are not prompted to set a data privacy level for this data source. Why?
The “None” data privacy level means that no privacy level has been set for this exact data source. However, when this happens the engine checks to see if a data privacy level has been set for the folder that this file is in and then for all folders up to the root. In this case, since the data privacy level has been set to Public for the c:\ drive, all files in all folders on that drive that have a data privacy level set to None (like this one) will inherit the c:\ drive’s setting of Public:
The same goes for databases on a SQL Server instance: they can inherit the data privacy settings set for the instance. The same is also true for web services, where data privacy settings can be set for different parts of a URL; for example, here’s the list of options for a call to the https://data.gov.uk/api/3/action/package_search web service described in part 2 of this series:
The general rule is that the engine looks for permissions for the exact data source that it’s trying to access, and if none are set then it keeps looking for more general permissions until it runs out of places to look.
In my opinion, I don’t think the way the “None” privacy level and inheritance works is very clear right now – it makes sense now I’ve had it explained to me, but the UI does nothing to help you understand what’s going on. Luckily it sounds like the dev team are considering some changes to make it more transparent. I would like to see the fact that data privacy levels have been inherited for a data source, and where they have been inherited from, called out in the Edit Permissions dialog.