Correct parameter binding for SELECT WHERE .. LIKE using fmdb? - sqlite

First time user of fmdb here, trying to start off doing things correctly. I have a simple single table that I wish to perform a SELECT WHERE .. LIKE query on and after trying several of the documented approaches, I can't get any to yield the correct results.
e.g.
// 'filter' is an NSString * containing a fragment of
// text that we want in the 'track' column
NSDictionary *params =
[NSDictionary dictionaryWithObjectsAndKeys:filter, #"filter", nil];
FMResultSet *results =
[db executeQuery:#"SELECT * FROM items WHERE track LIKE '%:filter%' ORDER BY linkNum;"
withParameterDictionary:params];
Or
results = [db executeQuery:#"SELECT * FROM items WHERE track LIKE '%?%' ORDER BY linkNum;", filter];
Or
results = [db executeQuery:#"SELECT * FROM items WHERE track LIKE '%?%' ORDER BY linkNum;" withArgumentsInArray:#[filter]];
I've stepped through and all methods converge in the fmdb method:
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args
Depending on the approach, and therefore which params are nil, it then either calls sqlite3_bind_parameter_count(pStmt), which always returns zero, or, for the dictionary case, calls sqlite3_bind_parameter_index(..), which also returns zero, so the parameter doesn't get slotted into the LIKE and then the resultSet from the query is wrong.
I know that this is absolutely the wrong way to do it (SQL injection), but it's the only way I've managed to have my LIKE honoured:
NSString *queryString = [NSString stringWithFormat:#"SELECT * FROM items WHERE track LIKE '%%%#%%' ORDER BY linkNum;", filter];
results = [db executeQuery:queryString];
(I've also tried all permutations but with escaped double-quotes in place of the single quotes shown here)
Update:
I've also tried fmdb's own …WithFormat variant, which should provide convenience and protection from injection:
[db executeQueryWithFormat:#"SELECT * FROM items WHERE track LIKE '%%%#%%' ORDER BY linkNum;", filter];
Again, stepping into the debugger I can see that the LIKE gets transformed from this:
… LIKE '%%%#%%' ORDER BY linkNum;
To this:
… LIKE '%%?%' ORDER BY linkNum;
… which also goes on to return zero from sqlite3_bind_parameter_count(), where I would expect a positive value equal to "the index of the largest (rightmost) parameter." (from the sqlite docs)

The error was to include any quotes at all:
[db executeQuery:#"SELECT * FROM items WHERE track LIKE ? ORDER BY linkNum;", filter];
… and the % is now in the filter variable, rather than in the query.

Related

DynamoDB Java SDK query to match items in a list

I'm trying to use SQL IN clause kind of feature in dynamoDB. I tried using withFilterExpression but I'm not sure how to do it. I looked at similar questions as they were too old. Is there a better method to do this? This is the segment of code I have got. I have used a static List as example but it is actually dynamic.
def getQuestionItems(conceptCode : String) = {
val qIds = List("1","2","3")
val querySpec = new QuerySpec()
.withKeyConditionExpression("concept_id = :c_id")
.withFilterExpression("question_id in :qIds") // obviously wrong
.withValueMap(new ValueMap()
.withString(":c_id", conceptCode));
questionsTable.query(querySpec);
}
I need to pass qID list to fetch results similar to IN clause in SQL Query.
Please refer to this answer. Basically you need to form key list/value list dynamically
.withFilterExpression("question_id in (:qId1, :qId2, ... , :qIdN)")
.withValueMap(new ValueMap()
.withString(":qId1", ..) // just do this for each element in the list in a loop programmatically
....
.withString(":qIdN", ..)
);
Mind there is a restriction on maxItems in 'IN'

Populating an Apex Map from a SOQL query

// I have a custom metadata object named boatNames__mdt and I'm using two methods to get a list of picklist values in a String[];
First Method
Map<String, boatNames__mdt> mapEd = boatNames__mdt.getAll();
string boatTypes = (string) mapEd.values()[0].BoatPick__c;
// BoatPick__c is a textarea field (Eg: 'Yacht, Sailboat')
string[] btWRAP = new string[]{};
**btWRAP**.addAll(boatTypes.normalizeSpace().split(','));
Second Method
string[] strL = new string[]{};
Schema.DescribeFieldResult dfr = Schema.SObjectType.boatNames__mdt.fields.BoatTypesPicklist__c;
// BoatTypesPicklist__c is a picklist field (Picklist Values: 'Yacht, Sailboat')
PicklistEntry[] picklistValues = dfr.getPicklistValues();
for (PicklistEntry pick : picklistValues){
**strl**.add((string) pick.getLabel());
}
Map with SOQL query
Map<Id, BoatType__c> boatMap = new Map<Id, BoatType__c>
([Select Id, Name from BoatType__c Where Name in :btWRAP]);
When I run the above Map with SOQL query(btWRAP[]) no records show up.
But when I used it using the strl[] records do show up.
I'm stunned!
Can you please explain why two identical String[] when used in exact SOQL queries behave so different?
You are comparing different things so you get different results. Multiple fails here.
mapEd.values()[0].BoatPick__c - this takes 1st element. At random. Are you sure you have only 1 element in there? You might be getting random results, good luck debugging.
normalizeSpace() and trim() - you trim the string but after splitting you don't trim the components. You don't have Sailboat, you have {space}Sailboat
String s = 'Yacht, Sailboat';
List<String> tokens = s.normalizeSpace().split(',');
System.debug(tokens.size()); // "2"
System.debug(tokens); // "(Yacht, Sailboat)", note the extra space
System.debug(tokens[1].charAt(0)); // "32", space's ASCII number
Try splitting by "comma, optionally followed by space/tab/newline/any other whitespace symbol": s.split(',\\s*'); or call normalize in a loop over the split's results?
pick.getLabel() - in code never compare using picklist labels unless you really know what you're doing. Somebody will translate the org to German, French etc and your code will break. Compare to getValue()

TopK function over Distributed Engine In clickhouse returns only 10 records

I'm running the following query
select topK(30)(Country) from distributed_table
note: distributed_table's engine is Distributed.
and even though there are over 100 possible "country" values, the query returns only 10.
Also, when I run it on local table , I'm getting more than 10 results.
Have I missed out some crucial configuration?
It looks like the problem occurs when intermediate results from shards are combined to the final result.
Let's check the results from each shard (will use distributed_group_by_no_merge-setting to disable the merging of intermediate results from each shard):
select any(_shard_num), topK(30)(Country)
from distributed_table
SETTINGS distributed_group_by_no_merge = 1
On each shard, the topK-function works correctly so as a workaround you can combine all intermediate results manually:
SELECT arrayDistinct(
arrayMap(x -> x.1,
/* sort values by frequency */
arraySort(x -> x.2,
/* converts an array of arrays to a flat array */
flatten(
/* group results from shards to one array */
groupArray(
/* assign each value the index number */
arrayMap((x, index) -> (x, index), shard_result, arrayEnumerate(shard_result))))))) ordered_value
FROM (
select topK(30)(Country) AS shard_result
from distributed_table
SETTINGS distributed_group_by_no_merge = 1)

issue with paginated reading datastore

I have a weired issue, I can't believe such a common feature could be broken (the error is certainely on my side), but I can't find how to make it work. I want to use the cursor from datastore to get paginated results, I keep getting all of them whatever i do
FetchOptions fetchOptions = FetchOptions.Builder.withChunkSize(5).prefetchSize(6);
String datastoreCursor = filter.getDatastoreCursor();
if (datastoreCursor != null) {
fetchOptions = fetchOptions.startCursor(Cursor.fromWebSafeString(datastoreCursor));
}
QueryResultList<Entity> result = preparedQuery.asQueryResultList(fetchOptions);
ArrayList<Product> productList = new ArrayList<Product>();
// int count = 0;
for (Entity entity : result) {
// if (++count == PRODUCTS_PER_PAGE)
// break;
Key key = entity.getKey();
productList.add(populateProduct(key.getId(), true, entity));
}
toReturn.setDatastoreCursor(result.getCursor());
Also if I don't read the rows (uncomment the lines with counter) and get the cursor the resulting cursor is the same. I thought it might bring me back to the last read element under the datastabase cursor (thinking result.getCursor() reflects the state of the db cursor)
I'm getting a cursor with this value E-ABAOsB8gEQbW9kaWZpY2F0aW9uRGF0ZfoBCQiIjsfAmKm_AuwBggIhagljaGF0YW1vamVyFAsSB1Byb2R1Y3QYgICAgICosgsMFA that points to no more elements (I have 23 elements for my test that I all receive from the first query)
When you use a QueryResultList, the requested cursor will always point to the end of the list. As specified by the javadoc of QueryResultList#getCursor:
Gets a Cursor that points to the result immediately after the last one in this list.
Even though you provide a prefetch and chunk size, The entire result list will still have all of your results since you have not specified a limit. Thus, the expected cursor is the cursor after the final element.
If you only want a specific number of entities per page, you should set a limit on the FetchOptions using the limit method. Then when you call getCursor(), you'll get a cursor at the end of your page, as opposed to the end of your dataset.
Instead, you could also use a QueryResultIterator. Unlike the QueryResultList, calling getCursor on a QueryResultIterator will result in the cursor that points after the last entity retrieved by calling .next() (javadoc).

ArrayCollection reverses order of my objects

I'll show you the function first.
private var areaCollection:ArrayCollection;
private function generateAreaCollection():void
{
areaCollection = new ArrayCollection();
areaCollection.addItem({Areal: "Totalareal:", Verdi: int(totalArea * 100) / 100 + " kvm"});
areaCollection.addItem({Areal: "Hovedtakets areal:", Verdi: int(result.area* 100) / 100 + " kvm"});
//Lots of other stuff omitted (just more addItems).
}
As you see, the order i put the items in the ArrayCollection is Areal, then Verdi (area, value)
When i loop through the collection later, the order changes. Now it is Verdi then Areal (value, area).
Does anyone have an idea of what might be the problem?
It really ruins things when I pass it over to a PHP-script for table-making in html later.
(I have several different dynamic DataGrids that differs in size and "values", so I can't really point directly to f.ex "Areal" in my PHP-script)
And by the way, does anyone know how i can remove that pesky mx_internal_uid?
Raw objects in AS3 (Object class, or things defined by {} ), have no sorting. That is, the order is unpredictable. You're confusing two ideas here I think. The Areal and Verdi strings are keys within an object map. The array collection is a list composed of two such objects. So any sorting applied to the array collection will sort those objects, but not within the object.
So you need to refer to areaCollection.source[0].Areal to get "Totalareal". And areaCollection.source[1].Verdi to get int(result.area* 100) / 100 + " kvm"
If you do for(var s:String in areaCollection.source[0]) { } you will iterate twice, with the value of "s" being "Areal" and "Verdi" or vice-versa (e.g, order not guaranteed).
Make sense? If you need order, you can make the object an array instead ["Totalareal", (int(totalArea * 100) / 100 + " kvm")], and then access "Verdi" using [1].
P.s. not sure how to remove the mx_internal_id.

Resources