how to insert new row by using executeUpdate - FMdatabase - ios

I'm using fmdb library.
https://github.com/ccgus/fmdb
I would like to insert new records but I don't know how can I do:
if let db = database, let q = db.executeUpdate("insert into \(table) (catid,subcat_id,id,subcat_title,title,description,lat,lon,takhfif,images,wifi,apple_health,wc,full_time,pos,work_hours,phone,mobile,fax,website,email,address,facebook,instagram,linkedin,telegram,googleplus,twitter,publish,feature,manager,city,rating_sum,rate_count,lastip,parking,isMallID,mallID,discount_images,price_images,newProduct_images,services_images,order_online,out_upon,cat_title,cat_icon,last_modify,item_logo,cat_logo,rate_sum1,rate_sum2,rate_sum3,rate_count1,rate_count2,rate_count3,rate_title1,rate_title2,rate_title3,rate_enable) values (\(catid),\(subcat_id),\(id),'\(subcat_title)','\(title)','\(description)','\(lat)','\(lon)','\(takhfif)','\(images)',\(wifi),\(apple_health),\(wc),\(full_time),\(pos),'\(work_hours)','\(phone)','\(mobile)','\(fax)','\(website)','\(email)','\(address)','\(facebook)','\(instagram)','\(linkedin)','\(telegram)','\(googleplus)','\(twitter)',\(publish),\(feature),'\(manager)','\(city)',\(rating_sum),\(rate_count),'\(lastip)',\(parking),\(isMallID),\(mallID),'\(discount_images)','\(price_images)','\(newProduct_images)','\(services_images)',\(order_online),\(out_upon),'\(cat_title)','\(cat_icon)',\(last_modify),'\(item_logo)','\(cat_logo)',\(rate_sum1),\(rate_sum2),\(rate_sum3),\(rate_count1),\(rate_count2),\(rate_count3),'\(rate_title1)','\(rate_title2)','\(rate_title3)',\(rate_enable))") {
}
but I got this message before building:
cannot invoke executeUpdate with an argument list of type string

You used the api wrong. The first parameter is the sql, each value has to be represented with an "?". The second one (values) is an array of the values. Try this:
if let db = database, let q = db.executeUpdate(
"insert into \(table) (catid,subcat_id,id,subcat_title,title,description,lat,lon,takhfif,images,wifi,apple_health,wc,full_time,pos,work_hours,phone,mobile,fax,website,email,address,facebook,instagram,linkedin,telegram,googleplus,twitter,publish,feature,manager,city,rating_sum,rate_count,lastip,parking,isMallID,mallID,discount_images,price_images,newProduct_images,services_images,order_online,out_upon,cat_title,cat_icon,last_modify,item_logo,cat_logo,rate_sum1,rate_sum2,rate_sum3,rate_count1,rate_count2,rate_count3,rate_title1,rate_title2,rate_title3,rate_enable) values (?, ?, ? ...., ?, ?, ? )"
values: [catid, subcat_id, id, subcat_title, title, description, lat, lon, takhfif, images, wifi, apple_health, wc, full_time, pos, work_hours, phone, mobile, fax, website, email, address, facebook, instagram, linkedin, telegram, googleplus, twitter, publish, feature, manager, city, rating_sum, rate_count, lastip, parking, isMallID, mallID, discount_images, price_images, newProduct_images, services_images, order_online, out_upon, cat_title, cat_icon, last_modify, item_logo, cat_logo, rate_sum1, rate_sum2, rate_sum3, rate_count1, rate_count2, rate_count3, rate_title1, rate_title2, rate_title3, rate_enable]) {
}
Note: I didn't count the values, so you have to ensure that the count of "?" inside of the string is exactly the count of your values!
Maybe you should think about a function to improve the readability of your code. Something like that:
extension FMDatabase {
func insert(into table: String, row values: [(column: String, content: Any)]) {
guard (values.count > 0) else { fatalError(values.count > 0, "Inserting nothing into a table makes no sense!") }
var sql = "INSERT INTO \(table) ("
var contents: [Any] = []
for (index, value) in values.enumerated() {
sql += index == 0 ? value.column : ", \(value.column)"
contents.append(value.content)
}
sql += ") VALUES ("
for index in 0..<values.count {
sql += index == 0 ? "?" : ", ?"
}
sql += ")"
executeUpdate(sql, values: contents)
}
}
Then you can insert values like that:
if let db = database {
db.insert(
into: table,
row: [
(column: "catid", content: catid),
(column: "subcat_id", content: subcat_id),
...
(column: "rate_enable", content: rate_enable)
]
)
}
Isn't it better readable?

Related

How can I insert data inside parentheses? Dart

There are inputs:
String SQL = "INSERT INTO table () VALUES ()";
List data1 = ['user_id', 'user_name'];
List data2 = ['1', 'test']
How can I get "INSERT INTO table (user_id, user_name) VALUES (1, test)"? Thank you.
You can do this with String interpolation ${}
void main() {
List data1 = ['user_id', 'user_name'];
List data2 = ['1', 'test'];
String SQL = "INSERT INTO table (${data1[0]}, ${data1[1]}) VALUES (${data2[0]}, ${data2[1]})";
print(SQL);
}
DartPad sample: https://dartpad.dev/?id=39a56cbf2b5cab254902ac7aa3a4324f

functional construct for flattening array for multiple row insert sql query

Is there a way to generate multiple row sql query (values only) using some functional constructs of array?
I've an array of Roles that I want to insert into sqlite database.
struct Role {
var id: Int32
var name: String?
}
func updateByUserId(_ id: Int32, _ roles: [Role]) {
let sql = "INSERT INTO user_role(user_id, role_id) VALUES( \(id), \(roles.map..) )"
}
Expectation:
for instances if id is 1 and roles has an array [10, 11, 14, 15]
Generated SQL should be
INSERT INTO user_role(user_id, role_id) VALUES(1, 10), (1, 11), (1, 14), (1, 15)
SQL Syntax for Multiple row insert is
INSERT INTO MyTable ( Column1, Column2 ) VALUES(Value1, Value2),
(Value1, Value2)
You can map each role to the string (id, role), then join the array of strings with the separator ,:
let values = roles.map { "(\(id), \($0))" }.joined(separator: ", ")
let sql = "INSERT INTO user_role(user_id, role_id) VALUES\(values)"
Although for this particular scenario the SQL string computation is not problematic, it's good practice to use parametrized statements for every DB query.
Working exclusively with parametrized statements avoids vulnerabilities like SQL injection, or malformed queries that fail to execute (when dealing with strings instead of ints).
So, I'd recommend going via the above route by writing something like this:
func updateByUserId(_ id: Int32, _ roles: [Role]) -> (statement: String, params: [Int32]) {
let statement = "INSERT INTO user_role(user_id, role_id) VALUES " + Array(repeating: "(?, ?)", count: roles.count).joined(separator: ", ")
let params = roles.flatMap { [id, $0.id] }
return (statement, params)
}
For your example in the question, the output would be something like this:
(statement: "INSERT INTO user_role(user_id, role_id) VALUES (?, ?), (?, ?), (?, ?), (?, ?)", params: [1, 10, 1, 11, 1, 14, 1, 15])
You can then use the SQLite functions to create the parametrized statement and bind the given values to it.
P.S. There is also the matter of validating that the array of roles is not empty, in which case you'd get an invalid SQL as output. To handle this, you can make the function return an optional, and nil will signal an empty array. Doing this will enable a small performance improvement, as you'll be able to use String(repeating:count:), which is a little bit faster than creating an array and joing it later on:
func updateByUserId(_ id: Int32, _ roles: [Role]) -> (statement: String, params: [Int32])? {
guard !roles.isEmpty else { return nil }
return (statement: "INSERT INTO user_role(user_id, role_id) VALUES (?, ?)" + String(repeating: ", (?, ?)", count: roles.count - 1),
params: roles.flatMap { [id, $0.id] })
}

Getting stackoverflow on recursion

I want to generate INSERT statement from JSON. The problem that I have problem with recursion (in section where I am getting nested elements). it's seems that I am getting SO.
import 'dart:convert';
main() {
Map jsonMap = jsonDecode(
'{"id":"1","name":"sample","Lots":[{"id":"1","title":"books","objs":[{"book":"name"}]}]}');
KVToTableInsert(String tablename, Map jsonMap) {
List<String> insertNoticeKeys = [];
List<String> insertNoticeValues = [];
jsonMap.forEach((key, value) {
if (value is List) // nested
{
KVToTableInsert(key, jsonMap); // if comment this line all work
} else {
insertNoticeKeys.add(key);
insertNoticeValues.add(value);
}
});
String sql = "INSERT INTO $tablename (" +
insertNoticeKeys.map((e) => '"$e"').join(', ') +
") VALUES (" +
insertNoticeValues.map((e) => "'$e'").join(', ') +
")";
print(sql);
}
KVToTableInsert("RootTable", jsonMap);
}
I expected to get 3 INSERT statment:
INSERT INTO RootTable ("id", "name") VALUES ('1', 'sample')
INSERT INTO Lots ("id", "title") VALUES ('1', 'books')
INSERT INTO Objs ("book") VALUES ('name')
But I am getting crush on this app.
There was a slight problem in your code, basically in this part:
if (value is List){
KVToTableInsert(key, jsonMap); // if comment this line all work
}
If you closely, take a look at your KVToTableInsert, you're passing arguments Map and not list. Which fails your recursion.
What you need to do is to pass the item of the array, into your KVToTableInsert, and this will work as expected
FINAL SOLUTION
// taking your jsonMap directly, and not decoding it
void main() {
Map jsonMap = {
"id":"1",
"name":"sample",
"Lots":[
{
"id": "1",
"title":"books",
"objs":[
{
"book":"name"
}
]
}
]
};
void KVToTableInsert(tablename, jsonMap){
List<String> insertNoticeKeys = [];
List<String> insertNoticeValues = [];
jsonMap.forEach((key,value){
if (value is List)
{
// Here is the magic, you need to iterate over your list, and pass MAP, not LIST
value.forEach((item){ KVToTableInsert(key, item);});
} else {
insertNoticeKeys.add(key);
insertNoticeValues.add(value);
}
});
String sql = "INSERT INTO $tablename (" +
insertNoticeKeys.map((e) => '"$e"').join(', ') +
") VALUES (" +
insertNoticeValues.map((e) => "'$e'").join(', ') +
")";
print(sql);
}
KVToTableInsert("Rootable", jsonMap);
}
OUTPUT
INSERT INTO objs ("book") VALUES ('name')
INSERT INTO Lots ("id", "title") VALUES ('1', 'books')
INSERT INTO Rootable ("id", "name") VALUES ('1', 'sample')
Also, look at the Dart Programming Language Naming Conventions, it is a good practise to use lowerCamelCase for your function/method name.

Doing joins with Fluent in a Vapor application

I'm struggling to figure out how to join my two tables together using fluent. In essence I want to run this SQL command:
SELECT p.name, o.amount, o.amount * p.amount total
FROM "OrderPoints" o
INNER JOIN "Points" p ON o.points_id = p.id
WHERE order_id = 10831
I've got my two models setup like so:
final class OrderPoint: Codable, PostgreSQLModel, Content, Migration {
var id: Int? = nil
var orderID: Order.ID
var pointID: Point.ID
var amount: Double = 0
var point: Parent<OrderPoint, Point> {
return parent(\.pointID)
}
}
final class Point: Codable, PostgreSQLModel, Content, Migration {
var id: Int? = nil
var name: String
var abbrev: String
var amount: Double
}
So now in my controller I loop over all of the orders that I care about:
// SELECT * FROM orders WHERE "month" = '2018-05-01'
let orders = try Order.query(on: req).filter(\Order.month == month).all()
return orders.flatMap(to: View.self) { orders in
let content = try orders.map { (order) -> OrderIndexContent in
let values = try order.orderPoints.query(on: req).all()
and I think that gets me all the items from order_points for the current ID, but I am just not seeing how to join it with the Points model so that I can do the rest of the query (i.e. get the multiplied amount and the point name)
An Order has multiple OrderPoint items.
An OrderPoint has a single Point item.
A Point could point to many OrderPoint items.
So in the way I think fluent refers to things, OrderPoints is not a pivot, as it's not a many to many.
This can't be right.
The following seems to be working, but there's no way this is the "right" way to do it as this would be massive amounts of extra SQL calls, right?
_ = try! order.orderPoints
.query(on: req)
.all()
.map(to: Void.self) { pointsForOrder in
pointsForOrder.forEach { orderPoint in
_ = try! orderPoint.point.get(on: req).map(to: Void.self) {
print("\(order.id!) \(order.name) \($0.abbrev) \(orderPoint.pointID) = \(orderPoint.amount) = \($0.amount * orderPoint.amount)")
}
}
}
In order to retrieve all the Points referenced in the OrderPoints from an Order you would have to query Point and join OrderPoint on it. Then you can add a filter on OrderPoint filtering the order you want to query.
This would result in the following Fluent query:
Point.query(on: req)
.join(field: \OrderPoint.pointID)
.filter(OrderPoint.self, \OrderPoint.orderID == order.id!)
.all()
This query would however only return an array of Points, so you would miss the information from OrderPoint as you pointed out in our discussion on Discord we would also have to decode OrderPoint, luckily Fluent has a nice method for this: .alsoDecode. So the final query would look like this:
Point.query(on: req)
.join(field: \OrderPoint.pointID)
.filter(OrderPoint.self, \OrderPoint.orderID == order.id!)
.alsoDecode(OrderPoint.self)
.all()
This would return a tuple containing both Point and OrderPoint

Filtered tokenised search result with Linq and EF

I'm looking for a way to create a filtering system based upon tokenised query strings to return a list of farms.
The filtering mechanism would hopefully be flexible enough for me to supply the tokens in any order to return results.
The rules for search would be like this:
state:WA crop:Banana
would give me a filtered list of all farms in WA with the crop banana.
crop:Banana state:WA
should return the same result.
city:Albany crop:Banana
would give me a filtered list of all farms in Albany with the crop banana.
Each of the values supplied could be wrapped in quotation marks to allow space separated values to be grouped. e.g
city:"Mount barker" crop:Banana
would give me a filtered list of all farms in Mount Barker with the crop banana.
Furthermore any non tokenised queries would just look within a farms Details property to return the list of farms again with quotation marks combining multiple word queries.
---------------------------------------EDIT--------------------------------------------
My current search system using predicates is coded as follows. it's long (sorry) and is my first attempt though I'm hoping this could be refactored by some kind soul.
Many thanks in advance:
public ActionResult Search(string query, int? page)
{
IQueryable<Farm> farms = this.ReadOnlySession.All<Farm>();
if (!String.IsNullOrWhiteSpace(query))
{
// http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder
var predicate = PredicateBuilder.True<Farm>();
// We want to replace the spaces in quoted values here so we can split by space later.
// http://stackoverflow.com/questions/2148587/regex-quoted-string-with-escaped-quotes-in-c
Regex quoted = new Regex(#"""[^""\\]*(?:\\.[^""\\]*)*""");
foreach (var match in quoted.Matches(query))
{
query = query.Replace(match.ToString(), match.ToString().Replace(' ', '-'));
}
// Tidy up the query to remove "".
string[] splitQuery = HttpUtility.UrlDecode(query).Replace("\"", "").Split(' ');
Dictionary<string, string> tokenDictionary = new Dictionary<string, string>();
// Loop through our string[] and create a dictionary. Guids used to allow multiple keys
// of the same value.
Parallel.ForEach(splitQuery, subQuery =>
{
string[] tempArray = subQuery.Split(':');
if (tempArray.Length == 2)
{
tokenDictionary.Add(String.Format("{0}:{1}", tempArray[0], Guid.NewGuid()), tempArray[1]);
}
else
{
tokenDictionary.Add(String.Format("description:{0}", Guid.NewGuid()), subQuery);
}
});
// Loop through the dictionary and create our predicate.
foreach (KeyValuePair<string, string> item in tokenDictionary)
{
string value = item.Value.Replace('-', ' ');
string key = item.Key.Split(':')[0].ToUpperInvariant();
switch (key)
{
case "CROP":
value = Utilities.CreateSlug(value, OzFarmGuideConfig.RemoveDiacritics);
predicate = predicate.And(x => x.Crops.Any(y => value.Equals(y.Slug, StringComparison.OrdinalIgnoreCase)));
break;
case "STATE":
predicate = predicate.And(x => value.Equals(x.City.State.Name, StringComparison.OrdinalIgnoreCase));
break;
case "CITY":
value = Utilities.CreateSlug(value, OzFarmGuideConfig.RemoveDiacritics);
predicate = predicate.And(x => value.Equals(x.City.Slug, StringComparison.OrdinalIgnoreCase));
break;
default:
predicate = predicate.And(x => !String.IsNullOrWhiteSpace(x.Details) && x.Details.Contains(value));
break;
}
}
farms = farms.Where(predicate).OrderByDescending(x => x.Rating)
.ThenByDescending(x => x.RatingVotes);
PagedList<Farm> pagedFarms = new PagedList<Farm>(farms, page.HasValue ? page.Value - 1 : 0, 5);
return View(pagedFarms);
}
else
{
PagedList<Farm> pagedFarms = null;
return View(pagedFarms);
}
}
Just a guess, would the problem correct itself with the introduction of DefaultIfEmpty()?
default:
// This is not working at the mo. Getting a null exception when we try
// to initialise PagedList.
predicate = predicate.And(x => x.Details.DefaultIfEmpty().Contains(value));
break;

Resources