-
-
Notifications
You must be signed in to change notification settings - Fork 731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DatabasePool can't read from existing non-WAL database #102
Comments
Hello @blkbam. If this is the same trouble than #40, then the answer is the same: don't create several instances of DatabasePool that connect to a given database file. Review your application architecture. Don't create DatabasePool in temporary objects like UIViewController, since this implies that you can't guarantee that there is never more than one DatabasePool connected to a single database file. |
This is the same error however I'm not sure you are understanding the problem. There is only one connection per file. In this sample the connection is stored in a dictionary by database name and is retrieved each time. Running through the sample you will see the issue. This happens the very first time the database is accessed. I used two connections to illustrate. Running through the sample in #40 as well just one time you get the same issue. Opening a DatabasePool connection and reading from it simply doesn't work unless you have written to the database prior to the read. This isn't a concurrency problem. |
I admit that I don't happily jump in dozens of lines of pasted code when there is no clear declaration of any problem, and an explicit link to an already solved issue. Tagging answers with a 😕 does not help. Besides, what do you want to read from a empty database? Isn't it a sure condition for failure? What do you expect? |
How can an application know that the database is empty unless it reads from it first? Every database has sqlite_master in it so even in this sample it should not fail. With applications that dynamically create databases such as mine I can't blindly write to the database without ensuring tables exist. |
#40 was not solved, the previous poster used a different object however this is to say that an application cannot solely use a DatabasePool object for connections unless it either writes to the database first or uses a DatabaseQueue object instead. Others may have read #40 and figured they were using the code incorrectly however in the two examples provided to you, it is clear that there is a bug in the object for newly created databases. Unless DatabasePool should not be used for new/empty database reads to system tables. If so, please either provide proper guidance in your documentation or update the code to not explicitly call try! so that the caller can catch and handle the exception. |
Listen, @blkbam. If you have found a real issue, I'm really happy, because I like this library to be as bug-free as possible. Since you say that you are not facing a concurrency issue, then you are able to write a short and minimal sample code that reveals the problem. Please write a real bug report with steps to reproduce, expected output, actual output. Comments are not necessary. We'll then reopen this issue. |
Define real bug report? I've provided all that you asked in the original post. The only difference is that my class uses a ViewController which again done for demo purposes. Would you like me to attach a whole project? |
I was thinking that a sample code of a few lines would have been enough. But if your issue only reveals in a whole project, then it's even more crucial that you write a real bug report. |
This is a minimal sample while also following the intended use of your the pooling philosophy. A full project would just include all to other basic templated files. |
Have a nice day! |
Thanks. You as well. |
I just encountered something similar, so allow me to include a reduced sample and a dissection of what seems to be happening: import Foundation
import GRDB
func testDatabasePool() {
let databaseName: String = NSUUID().UUIDString // a fresh/new database file is used
let rootLibDirectory = NSSearchPathForDirectoriesInDomains( .LibraryDirectory, .UserDomainMask, true ).first as String!
do {
print("Opening database with name: \(databaseName).db")
let dbPool = try DatabasePool(path: "\(rootLibDirectory)/\(databaseName).db")
dbPool.read { db in
print("Never reaches here")
// execution never reaches here - exception occurs first
}
}
catch {
print ("Encountered Error: \(error)")
}
print ("Finished: testDatabasePool") // never reaches here
}
testDatabasePool() What seems to be happening: (Note: In this example, a unique database filename is generated each time it runs to ensure that the database file does not yet exist.) In DatabasePool.init:
This seems to be a quirk in the behavior of SQLite. On a database not already in WAL mode (in this example, a fresh database file), If the database file already exists and is already in WAL mode, then the initial attempt to create the writer in DatabasePool results in SQLite creating the Or in other words: If a database is not already in WAL-mode when opened, Either:
For SQLite to create the Since this can be triggered by at least two simple scenarios, I think it may be worth coding a workaround into DatabasePool for this odd SQLite behavior (i.e. to force SQLite to generate the Trigger 1: Using DatabasePool to open and read from a database that doesn't yet exist (prior to any writes). |
I believe that #99 may actually be due to this as well. (Trigger 2, above) |
Thanks for the investigation @swiftlyfalling! But the sample code you provide has a logic error, since it reads from an empty database. There's nothing to read. The error is thrown from the Well, the database is empty, so it has no schema, and I'm not surprised that there is an error. I admit that error 14 (SQLITE_CANTOPEN "Unable to open the database file") is unexpected. But I don't see any quirk in SQLite, nor any bug in GRDB here. Just application code that needs to be fixed.
This looks like another piece of information. Do you mean that this error also happens on a non-empty database? |
Yes, this is what you mean by "trigger 2":
OK we have a bug, and I could reproduce it. The trigger 1 reveals an application bug, and does not require specific handling. |
@swiftlyfalling @blkbam The fix has been released in v0.79.4. |
Thanks for the fix! I can confirm it is working as expected. |
You're welcome! |
Yup, fix looks good on my end too. 👍 |
I ran into the same issue as #40 and did a little digging. I appears that a DatabasePool connection fails if no SerializedDatabase exists for the connection yet. I wrote a sample which illustrates this below.
`
import UIKit
import GRDB
class ViewController: UIViewController
{
}
`
The text was updated successfully, but these errors were encountered: