Updating multiple rows using instr - swift

I am trying to make the following call:
UPDATE MyTable SET path = ? WHERE instr(title, ?) AND start - ? < 60
However I have not been able to use instr with GRDB.
_ = try dbQueue?.write { db in
try MyTable
.filter(Column("start") > date - 60)
.filter(title.contains(Column("title")))
.updateAll(db,
Column("path").set(to: path)
)
}
How can I do this correctly? Could I also run a raw query instead? How can I fill the ? with my variables if using a raw query?

GRDB does not ship with built-in support for the instr function. You can define it in your code:
func instr(_ lhs: some SQLExpressible, rhs: some SQLExpressible) -> SQLExpression {
SQL("INSTR(\(lhs), \(rhs))").sqlExpression
}
// SELECT * FROM myTable WHERE instr(?, title)
let title: String = ...
let request = MyTable.filter(instr(title, Column("title")))
// UPDATE myTable SET path = ? WHERE instr(?, title)
let path: String = ...
try request.updateAll(db, Column("path").set(to: path))
See the How do I print a request as SQL? faq in order to control the SQL generated by GRDB.

Here is how I solved it with raw SQL in case it is too complicated to extend the framework.
I choose so, because I think this is easier to understand for someone who needs to read the code and has no experience with GRDB or frameworks in general.
do {
var dbQueue:DatabaseQueue? = try DatabaseQueue(path: "PATH_TO_DATABASE")
try dbQueue?.write { db in
try db.execute(
sql: "UPDATE MyTable SET path = :path WHERE instr(title, :title)",
arguments: ["path": path, "title": title]
)
}
} catch {
print("UPDATE MyTable \(error)")
}

Related

How can I use spotlight search from swift in the same way like I do it in terminal? NSMetadataQuery?

lets imagine that I need to know which apps was run in 1 week period.
Query for terminal looks like:
mdfind '(InRange(kMDItemFSContentChangeDate,$time.today(-7d),
$time.today(+1d)) && ((kMDItemContentTypeTree=com.apple.application) &&
InRange(kMDItemLastUsedDate,$time.today(-7d),$time.today(+1d))))'
so my query is:
'(InRange(kMDItemFSContentChangeDate,$time.today(-7d),
$time.today(+1d)) && ((kMDItemContentTypeTree=com.apple.application) &&
InRange(kMDItemLastUsedDate,$time.today(-7d),$time.today(+1d))))'
How can I run THIS query syntax from swift code and to receive URLs/paths as result of query?
I have seached for NSMetadataQuery but looks like I cannot use this search query as predicate in it...
Did I miss sth?
Or for query with such syntax I need to use some another instrument, but not NSMetadataQuery?
heh, there must be used MDQuery instead of NSMetadataQuery:
//set MDQuery string
let queryString = "(InRange(kMDItemFSContentChangeDate,$time.today(-7d),$time.today(+1d)) && ((kMDItemContentTypeTree=com.apple.application) && InRange(kMDItemLastUsedDate,$time.today(-7d),$time.today(+1d))))"
let query = MDQueryCreate(kCFAllocatorDefault, queryString as CFString, nil, nil)
//run the query
MDQueryExecute(query, CFOptionFlags(kMDQuerySynchronous.rawValue))
//loop through query results
for i in 0..<MDQueryGetResultCount(query) {
if let rawPtr = MDQueryGetResultAtIndex(query, i) {
let item = Unmanaged<MDItem>.fromOpaque(rawPtr).takeUnretainedValue()
//grab kMDItemPath value for each entry
if let path = MDItemCopyAttribute(item, kMDItemPath) as? String {
//search for certain TCC Protected Directory Paths
print(path)
}
}
}
( But there is exist also async search! This is sycn version )

Rust: Convert SQL types to Rust automatically using sqlx

I'm new to rust and was working on a generic database viewer app using SQLX. Now I've stumbled upon a problem. While querying like a simple
SELECT * FROM TABLE_NAME
Now the problem is that I don't know the type of rows and columns before hand. So how should I parse the queries?
I've tried the following from this post
for r in row {
let mut row_result: Vec<String> = Vec::new();
for col in r.columns() {
let value = r.try_get_raw(col.ordinal()).unwrap();
let value = match value.is_null() {
true => "NULL".to_string(),
false => {
let mat = value.as_str();
match mat {
Ok(m) => m.to_string(),
Err(err) => {
dbg!(err);
"ERROR".to_string()
}
}
}
};
// println!("VALUE-- {:?}", value);
row_result.push(value);
}
result.push(row_result);
}
The problem is that for some columns it's returning like this. Like for the ID columns
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0004"
and for some i'm getting the following error in the dbg! macro
Utf8Error {
valid_up_to: 2,
error_len: Some(
1,
),
}
Anyone can help me here?
BTW I'm using Postgres so all row types are of PgRow
2: Follow up. I was able to get the types of the columns from the information_schema but the problem seems to be that those will be in String and I couldn't find any way to convert those into rust types, like INT8 -> i64, TEXT -> String. Something like that.

SQLite.Swift - Querying an existing Db table

I am just now learning about SQLite.swift and was reading the documentation on it. I am trying to query an existing table that I already have but do not know how to do this. In the documentation it shows how to Query a table that is created (shown below https://github.com/stephencelis/SQLite.swift/blob/master/Documentation/Index.md#selecting-rows)
let users = Table("users")
try db.run(users.create { t in // CREATE TABLE "users" (
t.column(id, primaryKey: true) // "id" INTEGER PRIMARY KEY NOT NULL,
t.column(email, unique: true) // "email" TEXT UNIQUE NOT NULL,
t.column(name) // "name" TEXT
})
// )
for user in try db.prepare(users) {
print("id: \(user[id]), email: \(user[email]), name: \(user[name])")
// id: 1, email: alice#mac.com, name: Optional("Alice")
}
// SELECT * FROM "users"
I have an existing table that I am able to connect to it but the only way I'm able to get information from it is by doing db.scalar but not sure if this is the correct way of doing it.
let home = FileManager.default.homeDirectoryForCurrentUser
let dbURL = "Desktop/MyPracticeCode/EpubParser/"
let myPath = home.appendingPathComponent(dbURL)
let db = try Connection("\(myPath)/TestTable.sqlite3")
print(db.scalar("SELECT WtName FROM MyTable"))
this prints out the data I need but not sure if this is the correct approach. Is there a way to assign my existing table to a type "Table" and query it just like they did in the example. I searched everywhere online but couldn't find a clear answer. Thanks
I managed to figure out the answer. If anyone ran into this problem or the same question this is how I managed to solve it. Still not sure if it is the correct way. Basically you need to recreate the table with the columns just like in the example but with the name of your columns. Create a table and name it the exact name as the table in your db. Below is how I did it
let id = Expression<Int64>("id")
let Article = Expression<String?>("Article")
let ArticleName = Expression<String?>("ArticleName")
let ImageThumbnail = Expression<String?>("ImageThumbnail")
let WtCoverImage = Expression<String?>("WtCoverImage")
let myTable = Table("MyTable")
try db.run(myTable.create(ifNotExists: true){ t in
t.column(id,primaryKey: true)
t.column(Article)
t.column(ArticleName)
t.column(ImageThumbnail)
t.column(WtCoverImage)
})
This here is what I used:
try db.run(users.create(ifNotExists: true) { t in /* ... */ })
// CREATE TABLE "users" IF NOT EXISTS -- ...
Now I'm able to query it like this
for myData in try db.prepare(myTable){
print("WtCoverImage\(myData[WtCoverImage])")
}
class SqliteDB{
static let shared:SqliteDB = SqliteDB()
private init(){}
var DB:Connection!
/// path for creat a databased file.
func createDBPath(){
do {
let dbPath = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("chatDB.sqlite").path
DB = try Connection(dbPath)
print(dbPath)
} catch {
print(error.localizedDescription)
}
}
func dbTables_ChatMessage(){
let tableQuery = """
CREATE TABLE IF NOT EXISTS message_list (id INTEGER, conversation_id INTEGER UNIQUE, conversation_recipient_id INTEGER, timestamp INTEGER UNIQUE, content_type TEXT, message TEXT, user_id INTEGER, user_name TEXT, group_id INTEGER,isOnline TEXT,room_id INTEGER, local_conversation_id INTEGER ,room_unique_id TEXT,PRIMARY KEY(id AUTOINCREMENT))
"""
do {
try DB.run(tableQuery)
} catch {
print(error.localizedDescription)
}
}
}

How to make a select query using a local variable in SQLite on Swift?

In Swift, if you want to make a query from a local variable it is done by 'selectedButton'. So how is it possible in SQLite?
{
var selectedButton = 1;
let dbPath = NSBundle.mainBundle().pathForResource("practise2015", ofType:"sqlite3")
let db = FMDatabase(path: dbPath)
if db.open(){
let categoryLevelID = try! db.executeQuery("SELECT * FROM `LEVEL` WHERE CATEGORY_ID = " + selectedButton, values: nil)
}
}
Can't you just do something like this?
let categoryLevelID = try! db.executeQuery("SELECT * FROM LEVEL WHERE CATEGORY_ID = \(selectedButton.tag)", values: nil)
actuall you can't just pass a UIButton as an argument, you need some String, Int or Double value. Category_id sounds like an Int to me, so I guess you have the correct category_id in your UIButton as it's Tag
But you should do queries by using data binding anyway. Take a look at this tutorial by Ray Wenderlich: https://www.raywenderlich.com/123579/sqlite-tutorial-swift
Or this one: http://www.techotopia.com/index.php/An_Example_SQLite_based_iOS_8_Application_using_Swift_and_FMDB

SELECT * behaving oddly - Swift SQLite FMDB

I have a TEST table that appears to be behaving just weird. It simply consists of two integers, ID (auto increment) and LOCATIONSELECTED.
The table is populated
1, 99
2, 100
3, 101
etc
If I try and find LOCATIONSELECTED using the code below it works and returns 99!
func getTestSelectedRecord() {
sharedInstance.database!.open()
var sqlStatement = "SELECT * FROM TEST WHERE ID = 1"
var resultSet: FMResultSet! = sharedInstance.database!.executeQuery(sqlStatement, withArgumentsInArray: nil)
if (resultSet != nil) {
while resultSet.next() {
let getRecordID = resultSet.intForColumn("LOCATIONSELECTED")
NSLog("DatabaseFunctions - TEST - GET - getRecordID = %i", getRecordID)
}
}
sharedInstance.database!.close()
}
But if I remove the while loop it returns nul. I normally write my SELECT calls without the if != nil and while calls at all. There is only one record with an ID of 1 in the table.
I presume something subtle is going on here. Can anyone explain what it is?
You can remove the while loop, but you must maintain the:
resultSet.next();
statement, since is this the operation that allows to access the first (and only) record of the result.