David Sanford
David Sanford

Reputation: 745

SQLite insert into Swift causing loop Swift 3.1

I am trying to insert an array into SQLite with Swift 3.1

When I use the "if" statement, a single insert is achieved without issues. However, I need this to insert an Array, so I need to use the "while" statement. However, this is causing the insert statement to run in a loop.

func insertDiveGear() {
    var statement: OpaquePointer? = nil

    let names = itemDataArray

    let update = "INSERT INTO DiveGearForDive (id, name) VALUES (?, ?);"

    while sqlite3_prepare_v2(self.dlDatabase.database, update, -1, &statement, nil) == SQLITE_OK {

        for (_ , name) in names.enumerated() {

            let diveNumber = Int32(diveNumber)

            sqlite3_bind_int(statement, 1, diveNumber)
            sqlite3_bind_text(statement, 2, name, -1, nil)

            if sqlite3_step(statement) == SQLITE_DONE {
                print("Successfully inserted row.")
            } else {
                print("Could not insert row.")
            }

        }

        sqlite3_finalize(statement)
    }
}

Any help would be great

Upvotes: 1

Views: 721

Answers (1)

Rob
Rob

Reputation: 437452

Your code won't exit while loop because the sqlite3_prepare_v2 will not fail. But no while loop is needed. You can do something like:

func insertDiveGear() {
    var statement: OpaquePointer? = nil

    let names = itemDataArray

    let update = "INSERT INTO DiveGearForDive (id, name) VALUES (?, ?);"

    for (diveNumber, name) in names.enumerated() {
        if sqlite3_prepare_v2(dlDatabase.database, update, -1, &statement, nil) == SQLITE_OK {
            sqlite3_bind_int(statement, 1, Int32(diveNumber))
            sqlite3_bind_text(statement, 2, name, -1, SQLITE_TRANSIENT)

            if sqlite3_step(statement) == SQLITE_DONE {
                print("Successfully inserted row.")
            } else {
                print("Could not insert row.")
            }

            sqlite3_finalize(statement)
        }
    }
}

Or, even more efficient, prepare the statement only once, and then loop through binding, stepping, and then resetting:

func insertDiveGear() {
    var statement: OpaquePointer? = nil

    let names = itemDataArray

    let update = "INSERT INTO DiveGearForDive (id, name) VALUES (?, ?);"

    guard sqlite3_prepare_v2(dlDatabase.database, update, -1, &statement, nil) == SQLITE_OK else {
        let errmsg = String(cString: sqlite3_errmsg(dlDatabase.database))
        print("failure preparing: \(errmsg)")
        return
    }

    for (diveNumber, name) in names.enumerated() {
        sqlite3_bind_int(statement, 1, Int32(diveNumber))
        sqlite3_bind_text(statement, 2, name, -1, SQLITE_TRANSIENT)

        if sqlite3_step(statement) == SQLITE_DONE {
            print("Successfully inserted row.")
        } else {
            print("Could not insert row.")
        }

        sqlite3_reset(statement)
    }

    sqlite3_finalize(statement)
}

I didn't know what to make of that let diveNumber = Int32(diveNumber) line (where is diveNumber defined; using the same name?; etc.), so adjust that as you see fit, but the main question is why the loop didn't exit, and it's because sqlite3_prepare_v2 will always return SQLITE_OK if the SQL is well formed.

Also note, I used SQLITE_TRANSIENT constant (see Cannot invoke initializer for type 'sqlite3_destructor_type') in conjunction with sqlite3_bind_text to ensure that SQLite creates its own copy of the string that it received, which is prudent when dealing with Swift strings.

Upvotes: 2

Related Questions