xdenser / node-firebird-libfbclient

Firebird SQL binding
MIT License
82 stars 34 forks source link

FBblob::EIO_After_Read - invalid BLOB handle #80

Open mreis1 opened 7 years ago

mreis1 commented 7 years ago

I'm trying to push a blob to my database but until now I couldn't figure out what's going wrong.

For example: Having an open connection named tx.

# Sample Text Blob 
let d = tx.newBlobSync();
d._write(new Buffer('Hello World'), function (err) {
    console.log(err) **//<--- FBblob::EIO_After_Read - invalid BLOB handle**
    let ff = tx.prepareSync('update table set logo = ?'); //
    ff.execSync(d);
    tx.commitSync();
    tx.disconnect();
});
# Image Blob 
let d = tx.newBlobSync();
var data = fs.readFileSync(filePath);
d._write(data, function (err) {
    console.log(err) //<--- FBblob::EIO_After_Read - invalid BLOB handle
    let ff = tx.prepareSync('update table set logo = ?'); //
    ff.execSync(d);
    tx.commitSync();
    tx.disconnect();
});

Both Return the exactly same error. FBblob::EIO_After_Read - invalid BLOB handle

var data = fs.readFileSync(filePath);
d._write(data, data.length, function (err) {
    console.log(err)
    let ff = tx.prepareSync('update table set logo = ?'); //
    ff.execSync(d);
    tx.commitSync();
    tx.disconnect();
});

Using _writeSync


let d = tx.newBlobSync();
var data = fs.readFileSync(filePath);
try {
    d._writeSync(data, data.length);
} catch (e){
    console.log(e); // <---- a) **Error: In FBblob::_writeSync - invalid BLOB handle**
}

try {
    let ff = tx.prepareSync('update table set logo = ?');
    ff.execSync(d);
} catch (e){
    console.log(e); // <---- b) **Error: In FBStatement::execSync - BLOB not found** 
}

tx.commitSync();
tx.disconnect();

b) Since we got the error a) naturally b failed since the FBblob was empty
xdenser commented 7 years ago

probably you have not started transaaction in connection, i see a couple of bugs here :

  1. Error message is wrong :EIO_After_Read should be:EIO_After_Write
  2. newBlobSync should throw an exception if there is no active transaction
mreis1 commented 7 years ago

You are right @xdenser, I confused one of the particularities of the lib where a new transaction is only created upon call to con.query(....

Now I updated to code to ensure that a startNewTransaction is called but _writeSync() still throws invalid BLOB handle.

con.startNewTransaction(function(err, tx){
    if (err){
        throw err;
    }

    console.log({
        inTransaction: tx.inTransaction   // outputs { inTransaction: true }
    });

    let d = con.newBlobSync();
    let myFileData = fs.readFileSync(filePath);

    try {
        d._writeSync(myFileData);  // **<---- Error: In FBblob::_writeSync - invalid BLOB handle**
        writeToDatabase();
    } catch (e){
        console.log(e);
        tx.commitSync();
        tx.disconnect();
    }

    function writeToDatabase(){
        try {
            let ff = tx.prepareSync('update table set logo = ?');
            ff.execSync(d);
            tx.commitSync();
            tx.disconnect();
        } catch (e){
            console.log(e);
        }
    }
})
xdenser commented 7 years ago

Still getting error ? this is trange try to use approach as in the test https://github.com/xdenser/node-firebird-libfbclient/blob/9a86b568c52cd05e63df1760237e4305c4412608/tests/def/test-blob-stream.js#L49

mreis1 commented 7 years ago

@xdenser Thanks for the tip. I hadn't noticed the blob test in the tests folder. Following the same procedure worked like a charm.

FirebirdManager.pool.get()
    .then((tx) => {
        let ff = tx.prepareSync('update table set logo = ?'); 
        let d = tx.newBlobSync();
        let strm = new fb.Stream(d);
        let logoStrm = fs.createReadStream(filePath);

        logoStrm.pipe(strm);

        strm.on('close', function () {
            writeToDatabase();
        });

        strm.on('error', function (err) {
            console.log('error in write blob stream ', err);
        });

        logoStrm.on('end', writeToDatabase);

        function writeToDatabase() {
            try {
                ff.execSync(strm._blob);
                tx.commitSync();
                tx.disconnect();
            } catch (e) {
                console.log(e);
            }
        }
    })
    .catch(err => reject(err));

I noticed that con.prepareSync() must be called before creating the FBblob, so it will start the required transaction. But If I don't use the stream approach and if I try to write to the FBblob as show below (execSync(myBlob)) this will simply throw FBStatement::execSync - invalid BLOB ID

return new Promise((resolve, reject) => {
        FirebirdManager.pool.get()
            .then((tx) => {
                let ff = tx.prepareSync('update table set logo = ?');
                let d = tx.newBlobSync();
                let dataBuffer = fs.readFileSync(filePath);

                d._writeSync(dataBuffer);
                save(); 

                // or in async
                // d._write(dataBuffer, function(err){
                //     if (err){
                //         return rollback();
                //     }
                //     save();
                // });

                function save(){
                    try {
                        ff.execSync(d);
                        tx.commitSync();
                        tx.disconnect();
                        resolve();
                    } catch (e){
                       rollback(e); //<-------- In FBStatement::execSync - invalid BLOB ID
                    }

                }
                function rollback(e){
                    tx.rollbackSync();
                    tx.disconnect();
                    reject(e);
                }

            })
            .catch(err => reject(err));
    });

By the way, I forked the project and I did run all the tests.

test-events
....
✖ TensOfEvents

Assertion Message: We have 100 events
AssertionError: false == true
FAILURES: 1/740 assertions failed (469276ms)