I am trying to access function parameters within the 'case' statement in that function and displaying data/"filtered" based on the permission flag..Is it possible?
Usecase: TypeCast the value based on the columnType and check if the user has the permission to view the column based on which you display either the value or say something like "filtered"
Here is what I tried
function rls_columnCheck
.create-or-alter function rls_columnCheck(tableName:string, columnName: string, value:string, columnType:string, IsInGroupPII:bool, IsInGroupFinance:bool) {
let PIIColumns = rls_getTablePermissions(tableName, "PII");
let FinanceColumns = rls_getTablePermissions(tableName, "Finance");
let val= case(columnType=="bool", tobool(value),
columnType=="datetime", todatetime(value),
columnType=="int", toint(value),
value);
iif(columnName in (PIIColumns),
iif(columnName in (FinanceColumns),
iif(IsInGroupPII == true and IsInGroupFinance == true,
val,
"filtered"), // PII True, Fin True
iif(IsInGroupPII == true,
val,
"filtered") // PII True, Fin False
),
iif(columnName in (FinanceColumns),
iif(IsInGroupFinance == true,
val,
"filtered"), // PII False, Fin True
val // PII False, Fin False
)
);
}
Error:
Call to iff(): #then data type (int) must match the #else data type (string)
val in your function must have a single and well-defined data type, that is known at "compile" time of the query.
you can't have different cases, where in each it has a different type (bool, datetime, int, string - in your case statement) - hence the error.
if it makes sense in your use case, you can try to always have val typed as string.
This is not a good approach to use RLS because this will actually cause the engine to run a function for every column of every record. It has many downsides:
Performance of displaying the table’s contents (even if you have full permissions)
Queries on the table won’t benefit from the indexes Kusto stores (suppose you query PermissionTesting2 | where Col1 has “blablabla” - instead of checking the index for “blablabla”, the engine will have to scan all the data, because it has to apply a function for every single cell)
A better approach is to do something like this:
let UserCanSeePII = current_principal_is_member_of('aadgroup=group1#domain.com');
let UserCanSeeFinance = current_principal_is_member_of('aadgroup=group2#domain.com');
let ResultWithPII = YourTable | where UserCanSeePII and (not UserCanSeeFinance) | where ... | extend ...;
let ResultWithFinance = YourTable | where UserCanSeeFinance and (not UserCanSeePII) | where ... | extend ...;
let ResultWithPIIandFinance = YourTable | where UserCanSeeFinance and UserCanSeePII | where ... | extend ...;
let ResultWithoutPIIandFinance = YourTable | where (not UserCanSeePII) and (not UserCanSeeFinance) | where ... | extend ...;
union ResultWithPII, ResultWithFinance, ResultWithPIIandFinance, ResultWithoutPIIandFinance
Related
I am working on a simple database procedure in Kotlin using Room, and I can't explain why the process is so slow, mostly on the Android Studio emulator.
The table I am working on is this:
#Entity(tableName = "folders_items_table", indices = arrayOf(Index(value = ["folder_name"]), Index(value = ["item_id"])))
data class FoldersItems(
#PrimaryKey(autoGenerate = true)
var uid: Long = 0L,
#ColumnInfo(name = "folder_name")
var folder_name: String = "",
#ColumnInfo(name = "item_id")
var item_id: String = ""
)
And what I am just trying to do is this: checking if a combination folder/item is already present, insert a new record. If not, ignore it. on the emulator, it takes up to 7-8 seconds to insert 100 records. On a real device, it is much faster, but still, it takes around 3-4 seconds which is not acceptable for just 100 records. It looks like the "insert" query is particularly slow.
Here is the procedure that makes what I have just described (inside a coroutine):
val vsmFoldersItems = FoldersItems()
items.forEach{
val itmCk = database.checkFolderItem(item.folder_name, it)
if (itmCk == 0L) {
val newFolderItemHere = vsmFoldersItems.copy(
folder_name = item.folder_name,
item_id = it
)
database.insertFolderItems(newFolderItemHere)
}
}
the variable "items" is an array of Strings.
Here is the DAO definitions of the above-called functions:
#Query("SELECT uid FROM folders_items_table WHERE folder_name = :folder AND item_id = :item")
fun checkFolderItem(folder: String, item: String): Long
#Insert
suspend fun insertFolderItems(item: FoldersItems)
Placing the loop inside a single transaction should significantly reduce the time taken.
The reason is that each transaction (by default each SQL statement that makes a change to the database) will result in a disk write. So that's 100 disk writes for your loop.
If you begin a transaction before the loop and then set the transaction successful when the loop is completed and then end the transaction a single disk write is required.
What I am unsure of is exactly how to do this when using a suspended function (not that familiar with Kotlin).
As such I'd suggest either dropping the suspend or having another Dao for use within loops.
Then have something like :-
val vsmFoldersItems = FoldersItems()
your_RoomDatabase.beginTransaction()
items.forEach{
val itmCk = database.checkFolderItem(item.folder_name, it)
if (itmCk == 0L) {
val newFolderItemHere = vsmFoldersItems.copy(
folder_name = item.folder_name,
item_id = it
)
database.insertFolderItems(newFolderItemHere)
}
}
your_RoomDatabase.setTransactionSuccessful() //<<<<<<< IF NOT set then ALL updates will be rolled back
your_RoomDatabase.endTransaction()
You may wish to refer to:-
https://developer.android.com/reference/androidx/room/RoomDatabase
You may wish to especially refer to runInTransaction
Learning more about how to write a query in Kusto. I have a column in 2 tables that have different Roles, but the column header is Role, that I'd like to combine the data into one column called Roles.
I tried, adding this, | extend Roles = strcat (RoleName, Role), but that just combined the data.
Here is my query attempt, I'm joining 3 tables, 2 of which have the roles. The third is where I'm validating the user aliases.
(cluster('****').database('****').****_****** | where Discriminator == 'Service'| where DivisionOid == '******')
| join kind = leftouter cluster('****').database('****').Release_Users on SubscriptionId
| join kind = leftouter (cluster('****').database('****').Release_AzureAccess
| where RoleId contains "****" and PrincipalType !contains "ServicePrincipal") on SubscriptionId
| join kind = leftouter cluster('****').database('****').Headtrax_PeopleHierarchyV1 on $left.PrincipalName == $right.EmailAddress and $left.LiveEmailId == $right.EmailAddress
| extend Roles = strcat (RoleName, Role)<<--this was my failed attempt at combining the Role columns. That just concatenated.
I want to validate each user is active from 2 different tables against a person table. I'm a novice and am struggling with how to get this right. I'm thinking I want to combine the 2 tables into one list rather than trying to combine one column out of the 2 tables. Anyone have any advice?
This seems like a job for the union operator.
Union takes two or more tables and returns the rows of all of them.
From: https://learn.microsoft.com/en-us/azure/kusto/query/unionoperator?pivots=azuredataexplorer
Here's an example query from the docs above -
let View_1 = view () { print x=1 };
let View_2 = view () { print x=toint(2) };
let View_3 = view () { print x_long=3 };
union withsource=TableName View_1, View_2, View_3
produces:
The answer from Irwin will certainly work. If you want to take his solution and flatten it into one row, you could do that with the summarize function.
let View_1 = view () { print x=1 };
let View_2 = view () { print x=toint(2) };
let View_3 = view () { print x_long=3 };
union withsource=TableName View_1, View_2, View_3
| summarize sum(x_long1), sum(x_int), sum(x_long)
I am trying to use the type ImageURISource which is here - https://github.com/facebook/react-native/blob/26684cf3adf4094eb6c405d345a75bf8c7c0bf88/Libraries/Image/ImageSource.js#L15
type ImageURISource = {
uri?: string,
bundle?: string,
method?: string,
headers?: Object,
body?: string,
cache?: 'default' | 'reload' | 'force-cache' | 'only-if-cached',
width?: number,
height?: number,
scale?: number,
};
export type ImageSource = ImageURISource | number | Array<ImageURISource>;
However we see that it is exported as a union along with 2 other things. Is it possible to pick from a union just one?
I was hoping to do:
$Pick<ImageSource, ImageURISource>
It's not very pretty, but you could use refinement to specifically refine the type that you want out of it by doing something like this:
var source: ImageSource = {}
if (typeof source === "number" || Array.isArray(source)) throw new Error();
var uriSource = source;
type ImageURISource = typeof uriSource;
The downside here is that if the add more types to the union, your code would start failing again.
It seems like you'd be best off making a PR to react-native to expose that type.
I have some json, for examle:
data = {
"name":"Bob","age":"20",
"name":"Jo","age":"21",
"name":"Jo","age":"22",
"name":"Nick","age":"23"
}
Next, I use crossfilter, create dimension and filter it:
let ndx = crossfilter(data);
let dim = ndx.dimension(d => d.name).filter(d !== "Jo");
//try to get filtered values
let filtered = dim.top(Infinity); // -> return 2 values where 'name'!='Jo'
//"name":"Bob","age":"20"
//"name":"Nick","age":"23"
let myGroup = dim.group(d => {
if(d === 'Jo') {
//Why we come here? This values must be filtered already
}
})
How can I filter my dimension and don't have these values on 'dim.group'?
Not sure what version you are using, but in the current version of Crossfilter, when a new group is created all records are first added to the group and then filtered records are removed. So the group accessor will be run at least once for all records.
Why do we do this? Because for certain types of grouping logic, it is important for the group to "see" a full picture of all records that are in scope.
It is possible that the group accessor is run over all records (even filtered ones) anyway in order to build the group index, but I don't remember.
I am new to go and trying to retrieve data from a sqlite database.
Im using github.com/mattn/go-sqlite3 as sqlite driver.
The query I m sending does not return any results even though it should. I tried the query my programme is generating manually and it returns data as it should when I use the query manually as well as when I send it via my programme.
Here is my code:
for index := range Array {
id, _ := strconv.Atoi(Array[index])
rand.Seed(time.Now().UnixNano())
RandomNr := rand.Intn(100)
fmt.Printf("index: %d - randomnr: %d \n", id, RandomNr)
rows, errROW := db.Query("SELECT user.id,user.name,stage.link,player.url,player.characterchance,player.chance FROM user,stage,player WHERE user.id = '%d' AND '%d' <= user.chance AND stage.user = user.id AND stage.domain = player.id AND player.chance > 0 ORDER BY player.id ASC \n",id, RandomNr)//.Scan(&idr, &name, &link, &url, &characterchance, &chance)
//this is what the finished query looks like and it returns the rows as its supposed to
//rows, errROW := db.Query("SELECT user.id,user.name,stage.link,player.url,player.characterchance,player.chance FROM user,stage,player WHERE user.id = '203' AND '33' <= user.chance AND stage.user = user.id AND stage.domain = player.id AND player.chance > 0 ORDER BY player.id ASC")
if errROW != nil {
fmt.Println("errROW")
log.Println(errROW)
}
defer rows.Close()
if rows.Next() == false {
fmt.Println("no rows ")
}
for rows.Next() {
fmt.Println("where are the rows")
var id int
var name string
var link string
var url string
var characterchance int
var chance int
rows.Scan(&id, &name, &link, &url, &characterchance, &chance)
fmt.Println(id,name,link,url,characterchance,chance)
}
rows.Close()
}
}
This query can return multiple and single rows. I also tried retrieving the data via QueryRow as a single row which also did not return any result.
Any help would be much appreciated.
UPDATE:
I added
if rows.Next() == false
as an attempt to find the problem. Removing it yields the same result. Furthermore I do not get an error message from scan. The for rows.next() loop does not even get executed.
when you do:
if rows.Next() == false
you are scrolling to the first row
and
for rows.Next()
moves to the next row
basically, you are skipping the first row in your result set in the example code you provided.
also, you are ignoring the error in Scan.
This looks like it would print something if the query returns at least 2 rows (since first row is being skipped)
Ok I figured out what the problem was:
In my query I used: %d as a placeholder for my variable when I should have used $1,$2 etc. Using this the query returns results as expected.
It seems strange to me that this behaviour is allowed returns no error from go or sqlite and even works when you just printout the query and use it with sqlite3 manually. Coming from C and just starting out with go this can obviously be the cause for some headaches.