├── tests ├── .gitignore ├── testdata │ └── image.gif ├── createTestDB.sql ├── config.js ├── createTestDB.cmd ├── perf │ ├── select-full-sync.js │ ├── select-one-sync.js │ ├── select-full-async.js │ ├── select-one-async.js │ ├── select-prep-async.js │ ├── select-prep-async-trans.js │ ├── select-pool-async.js │ └── select-prep-async-pool.js └── def │ ├── test-concurrent-fetch.js │ ├── test-fbres.js │ ├── test-storedproc.js │ ├── test-concurrent-query.js │ ├── test-transaction.js │ ├── test-async.js │ ├── test-blob-stream.js │ ├── test-blob.js │ ├── test-sync.js │ ├── test-binding.js │ ├── test-datatypes.js │ ├── test-events.js │ └── test-prepared-stmt.js ├── .gitignore ├── .npmignore ├── fb ├── lib │ └── fbclient_ms.lib ├── lib64 │ └── fbclient_ms.lib └── include │ ├── ib_util.h │ └── perf.h ├── samples ├── fileman │ ├── static │ │ ├── elfinder │ │ │ ├── images │ │ │ │ ├── ql.png │ │ │ │ ├── icons-big.png │ │ │ │ ├── icons-big.psd │ │ │ │ ├── spinner.gif │ │ │ │ ├── toolbar.png │ │ │ │ └── icons-small.png │ │ │ └── css │ │ │ │ └── elfinder.css │ │ ├── css │ │ │ └── smoothness │ │ │ │ └── images │ │ │ │ ├── ui-icons_222222_256x240.png │ │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ │ ├── ui-icons_454545_256x240.png │ │ │ │ ├── ui-icons_888888_256x240.png │ │ │ │ ├── ui-icons_cd0a0a_256x240.png │ │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ │ │ └── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ └── index.html │ ├── fileman.js │ └── elfinder.js ├── testDisconnect │ ├── config.js │ ├── testCommit.js │ ├── test-disconnect-blob.js │ └── test-disconnect.js ├── deferred │ ├── sample.js │ └── fb-deferred.js └── simple-ws │ └── simple-ws.js ├── .gitmodules ├── package.json ├── src ├── fb-bindings-fbeventemitter.h ├── fb-bindings.h ├── fb-bindings.cc ├── fb-bindings-statement.h ├── fb-bindings-transaction.h ├── fb-bindings-blob.h ├── fb-bindings-fbresult.h ├── fb-bindings-fbeventemitter.cc ├── fb-bindings-eventblock.h ├── fb-bindings-connection.h ├── fb-bindings-transaction.cc ├── fb-bindings-statement.cc ├── fb-bindings-blob.cc └── fb-bindings-eventblock.cc ├── LICENSE ├── .travis.yml ├── binding.gyp ├── appveyor.yml ├── firebird.js └── README.md /tests/.gitignore: -------------------------------------------------------------------------------- 1 | current/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | test.js 3 | package-lock.json 4 | issue* 5 | .project 6 | /node_modules 7 | *.log 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test.js 2 | build 3 | issues 4 | tests 5 | tools 6 | node_modules 7 | .travis.yml 8 | appveyor.yml 9 | -------------------------------------------------------------------------------- /fb/lib/fbclient_ms.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/fb/lib/fbclient_ms.lib -------------------------------------------------------------------------------- /fb/lib64/fbclient_ms.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/fb/lib64/fbclient_ms.lib -------------------------------------------------------------------------------- /tests/testdata/image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/tests/testdata/image.gif -------------------------------------------------------------------------------- /tests/createTestDB.sql: -------------------------------------------------------------------------------- 1 | SET SQL DIALECT 3; 2 | create database 'TEST.FDB' USER 'SYSDBA' PASSWORD 'masterkey' page_size 8192 DEFAULT CHARACTER SET UTF8; 3 | -------------------------------------------------------------------------------- /samples/fileman/static/elfinder/images/ql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/elfinder/images/ql.png -------------------------------------------------------------------------------- /samples/fileman/static/elfinder/images/icons-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/elfinder/images/icons-big.png -------------------------------------------------------------------------------- /samples/fileman/static/elfinder/images/icons-big.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/elfinder/images/icons-big.psd -------------------------------------------------------------------------------- /samples/fileman/static/elfinder/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/elfinder/images/spinner.gif -------------------------------------------------------------------------------- /samples/fileman/static/elfinder/images/toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/elfinder/images/toolbar.png -------------------------------------------------------------------------------- /samples/fileman/static/elfinder/images/icons-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/elfinder/images/icons-small.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tools/nodeunit"] 2 | path = tools/nodeunit 3 | url = git://github.com/caolan/nodeunit.git 4 | [submodule "tools/node-deferred"] 5 | path = tools/node-deferred 6 | url = git://github.com/felixge/node-deferred.git 7 | -------------------------------------------------------------------------------- /samples/fileman/static/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/HEAD/samples/fileman/static/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /tests/config.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | 4 | See license text in LICENSE file 5 | */ 6 | const path = require('path'); 7 | 8 | exports.cfg = { 9 | // Database connection settings 10 | db: "127.0.0.1:" + path.join(__dirname,"..","build","test_db","TEST.FDB"), 11 | // db: "192.168.111.133:test.fdb", 12 | user: "sysdba", 13 | password: "masterkey", 14 | role: "" 15 | }; 16 | 17 | console.log(exports.cfg); 18 | 19 | -------------------------------------------------------------------------------- /samples/testDisconnect/config.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | 4 | See license text in LICENSE file 5 | */ 6 | const path = require('path'); 7 | 8 | exports.cfg = { 9 | // Database connection settings 10 | db: "127.0.0.1:" + path.join(__dirname,"..","..","build","test_db","TEST.FDB"), 11 | // db: "192.168.111.133:test.fdb", 12 | user: "sysdba", 13 | password: "masterkey", 14 | role: "" 15 | }; 16 | 17 | console.log(exports.cfg); 18 | 19 | -------------------------------------------------------------------------------- /tests/createTestDB.cmd: -------------------------------------------------------------------------------- 1 | set FB_PATH=C:\Program Files\Firebird\Firebird_2_5\bin\ 2 | set BUILD_DIR=%~dp0..\build\ 3 | set CREATE_SCRIPT=%~dp0..\tests\createTestDB.sql 4 | set DB_DIR=%BUILD_DIR%test_db\ 5 | set TEST_DB=%DB_DIR%TEST.FDB 6 | IF EXIST %DB_DIR% GOTO RECREATE_DB 7 | cd %BUILD_DIR% 8 | mkdir test_db 9 | :RECREATE_DB 10 | rem delete old db 11 | cd %DB_DIR% 12 | if NOT EXIST %TEST_DB% GOTO CREATE_DB 13 | del %TEST_DB% 14 | :CREATE_DB 15 | "%FB_PATH%isql" -input %CREATE_SCRIPT% 16 | 17 | IF EXIST %BUILD_DIR%Release\fbclient.dll GOTO FINISHED 18 | copy "%FB_PATH%fbclient.dll" %BUILD_DIR%Release\ 19 | :FINISHED 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/deferred/sample.js: -------------------------------------------------------------------------------- 1 | var fb = require('./fb-deferred.js'); 2 | 3 | var conn = fb.createConnection(); 4 | 5 | conn.connect('test.fdb','sysdba','masterkey','') 6 | .trycatch() 7 | .addCallback(function(){ 8 | console.log('connected'); 9 | return conn.query("select * from rdb$relations"); 10 | }) 11 | .addCallback(function(res){ 12 | console.log('fetching'); 13 | return res.fetch("all",true,function(row){ 14 | console.log(row); 15 | }); 16 | }) 17 | .addCallback(function(){ 18 | console.log('all fetched'); 19 | }) 20 | .addErrback(function(err) { 21 | console.log('error'); 22 | console.log(err); 23 | }); -------------------------------------------------------------------------------- /samples/testDisconnect/testCommit.js: -------------------------------------------------------------------------------- 1 | var cfg = require("./config").cfg; 2 | 3 | // Require modules 4 | var 5 | fb_binding = require("../../firebird.js"); 6 | 7 | 8 | 9 | var conn = fb_binding.createConnection(); 10 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 11 | if(conn.connected) { 12 | console.log("Connected to database"); 13 | } 14 | var res = conn.querySync("select * from rdb$relations"); 15 | console.log("Committin"); 16 | conn.commit(function(err){ 17 | console.log("Commit done ", err); 18 | }); 19 | 20 | setTimeout(function(){ 21 | console.log("after timeout"); 22 | 23 | }, 5000); 24 | 25 | -------------------------------------------------------------------------------- /samples/simple-ws/simple-ws.js: -------------------------------------------------------------------------------- 1 | var fb=require('../../firebird'); 2 | var http=require('http'); 3 | var sys =require('sys'); 4 | // Create Firebird Connection Object 5 | var con = fb.createConnection(); 6 | 7 | // Connect to Database 8 | con.connectSync('test.fdb','sysdba','masterkey',''); 9 | 10 | // Create HTTP server 11 | http.createServer(function(req,res){ 12 | // Query and fecth all rows 13 | var rows = con.querySync("select * from rdb$relations") 14 | .fetchSync('all',true); 15 | res.writeHead(200,{'Content-Type':'text/plain'}); 16 | // Return rows object (it is array) to browser 17 | res.end(sys.inspect(rows)); 18 | }).listen(8080); 19 | 20 | console.log('Server is running at http://localhost:8080'); -------------------------------------------------------------------------------- /samples/testDisconnect/test-disconnect-blob.js: -------------------------------------------------------------------------------- 1 | // Load configuration 2 | var cfg = require("./config").cfg; 3 | 4 | // Require modules 5 | var 6 | fb_binding = require("../../firebird.js"); 7 | 8 | 9 | 10 | var conn = fb_binding.createConnection(); 11 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 12 | if(conn.connected) { 13 | console.log("Connected to database"); 14 | } 15 | // var trans = conn.startNewTransactionSync(); 16 | var res = conn.querySync("select * from rdb$relations"); 17 | // trans.rollbackSync(); 18 | console.log("Query result 1", res) 19 | 20 | 21 | setTimeout(function() { 22 | console.log("Try again") 23 | var blob = conn.newBlobSync(); 24 | console.log("Blob", blob); 25 | 26 | 27 | }, 20000); 28 | 29 | -------------------------------------------------------------------------------- /samples/fileman/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Files 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 20 | 21 | 22 |
23 | 24 | -------------------------------------------------------------------------------- /tests/perf/select-full-sync.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | var fb = require('../../firebird'); 9 | var util = require('util'); 10 | 11 | var http = require('http'); 12 | 13 | http.createServer(function (req, res) { 14 | res.writeHead(200, {'Content-Type': 'text/plain'}); 15 | var con = fb.createConnection(); 16 | con.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 17 | // var rs = con.querySync('select * from rdb$relations'); 18 | var rs = con.querySync('select first 20 * from test_t where pid = 10'); 19 | var rows = rs.fetchSync("all",true); 20 | con.disconnect(); 21 | res.write('['); 22 | rows.forEach(function(r){ 23 | res.write(JSON.stringify(r)+','); 24 | }); 25 | res.end(']'); 26 | }).listen(1337, "127.0.0.1"); 27 | console.log('Server running at http://127.0.0.1:1337/'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firebird", 3 | "version": "v0.1.5-snapshot", 4 | "description": "Firebird binding to node, uses libfbclient.", 5 | "author": "Denys Khanzhiyev", 6 | "main": "./firebird", 7 | "maintainers": [ 8 | { 9 | "name": "Denys Khanzhiyev", 10 | "email": "xdenser@gmail.com" 11 | } 12 | ], 13 | "bugs": { 14 | "url": "http://github.com/xdenser/node-firebird-libfbclient/issues" 15 | }, 16 | "licenses": [ 17 | { 18 | "type": "MIT", 19 | "url": "https://raw.githubusercontent.com/xdenser/node-firebird-libfbclient/master/LICENSE" 20 | } 21 | ], 22 | "engines": { 23 | "node": ">=0.10" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "http://github.com/xdenser/node-firebird-libfbclient" 28 | }, 29 | "dependencies": { 30 | "nan": "^2.10.0" 31 | }, 32 | "devDependencies" : { 33 | "nodeunit": "^0.11.3" 34 | }, 35 | "scripts": { 36 | "test": "nodeunit tests/def" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/fileman/fileman.js: -------------------------------------------------------------------------------- 1 | var connect = require('connect'); 2 | var sys = require('sys'); 3 | var elfinder = require('./elfinder').elfinder; 4 | 5 | 6 | connect( 7 | function(req,res,next){ 8 | if(req.url=='/') req.url = '/index.html'; 9 | next(); 10 | }, 11 | connect.favicon(), 12 | connect.static(__dirname + '/static'), 13 | connect.bodyParser(), 14 | function(req,res,next){ 15 | if(req.url.match(/^\/node/)){ 16 | var meth = req.url.match(/^\/node\/(.+)[\?\/]/)[1]; 17 | // console.log('method '+meth); 18 | var respObj = { 19 | elfinder: function(){ 20 | elfinder(req, res); 21 | } 22 | }; 23 | if(meth in respObj) respObj[meth](); 24 | else next(); 25 | } 26 | else next(); 27 | }, 28 | function(req,res){ 29 | res.setHeader('Content-Type','text/html'); 30 | res.statusCode = 404; 31 | res.end("Not Found !"); 32 | } 33 | ).listen(8080); 34 | 35 | console.log('Server is running at http://localhost:8080'); -------------------------------------------------------------------------------- /src/fb-bindings-fbeventemitter.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright by Denys Khanzhiyev aka xdenser 3 | * 4 | * See license text in LICENSE file 5 | */ 6 | #ifndef SRC_FB_BINDINGS_FBEVENTEMITTER_H_ 7 | #define SRC_FB_BINDINGS_FBEVENTEMITTER_H_ 8 | #define BUILDING_NODE_EXTENSION 1 9 | 10 | #include "./fb-bindings.h" 11 | 12 | using namespace node; 13 | using namespace v8; 14 | 15 | 16 | class FBEventEmitter: public Nan::ObjectWrap { 17 | public: 18 | static void Initialize(v8::Local target); 19 | static Nan::Persistent constructor_template; 20 | void Emit(Local event, int argc, Local argv[]); 21 | 22 | protected: 23 | void start_async(); 24 | 25 | void stop_async(); 26 | 27 | FBEventEmitter (); 28 | 29 | /*static Local 30 | InAsyncGetter(Local property, 31 | const AccessorInfo &info); 32 | */ 33 | static NAN_GETTER(InAsyncGetter); 34 | 35 | static Nan::AsyncResource asyncResource; 36 | 37 | private: 38 | bool in_async; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /tests/perf/select-one-sync.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | var fb = require('../../firebird'); 9 | var util = require('util'); 10 | 11 | var http = require('http'); 12 | 13 | var con = fb.createConnection(); 14 | con.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 15 | 16 | http.createServer(function (req, res) { 17 | res.writeHead(200, {'Content-Type': 'text/plain'}); 18 | if(!con.inTransaction) con.startTransactionSync(); 19 | //var rs = con.querySync('select * from rdb$relations'); 20 | var rs = con.querySync('select * from test_t where pid = 10'); 21 | var rows = rs.fetchSync("all",true); 22 | res.write('['); 23 | rows.forEach(function(r){ 24 | res.write(JSON.stringify(r)+','); 25 | }); 26 | res.end(']'); 27 | con.commitSync(); 28 | }).listen(1337, "127.0.0.1"); 29 | console.log('Server running at http://127.0.0.1:1337/'); 30 | 31 | process.on('exit',function(){ 32 | con.disconnect(); 33 | }); -------------------------------------------------------------------------------- /tests/perf/select-full-async.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | var fb = require('../../firebird'); 9 | var util = require('util'); 10 | 11 | var http = require('http'); 12 | 13 | http.createServer(function (req, res) { 14 | res.writeHead(200, {'Content-Type': 'text/plain'}); 15 | var con = fb.createConnection(); 16 | con.connect(cfg.db, cfg.user, cfg.password, cfg.role,function(){ 17 | //con.query('select * from rdb$relations',function(err,rs){ 18 | con.query('select first 20 * from test_t where pid = 10',function(err,rs){ 19 | //var rows = []; 20 | res.write('['); 21 | rs.fetch("all",true,function(r){ 22 | //rows.push(r); 23 | res.write(JSON.stringify(r)+','); 24 | }, function(err){ 25 | con.disconnect(); 26 | //res.end(util.inspect(rows)); 27 | res.end(']'); 28 | }); 29 | 30 | }); 31 | }); 32 | 33 | }).listen(1337, "127.0.0.1"); 34 | console.log('Server running at http://127.0.0.1:1337/'); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | ----------- 3 | 4 | Copyright (C) 2011 Denys Khanzhiyev aka xdenser. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/fb-bindings.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright by Denys Khanzhiyev aka xdenser 3 | * 4 | * See license text in LICENSE file 5 | */ 6 | 7 | #ifndef SRC_FB_BINDINGS_H_ 8 | #define SRC_FB_BINDINGS_H_ 9 | 10 | // #include 11 | #include 12 | #include 13 | #include 14 | 15 | #if NODE_MODULE_VERSION < NODE_10_0_MODULE_VERSION 16 | # define FB_MAYBE_NEED_ISOLATE 17 | #else 18 | # define FB_MAYBE_NEED_ISOLATE Isolate::GetCurrent(), 19 | #endif 20 | 21 | #define MAX_ERR_MSG_LEN 1024 22 | #define ERR_MSG(obj,class) \ 23 | Nan::New(ErrorMessage(obj->status,obj->err_message,sizeof(obj->err_message))).ToLocalChecked() 24 | 25 | #define ERR_MSG_STAT(status,class) \ 26 | Nan::New(ErrorMessage(status,class::err_message,sizeof(class::err_message))).ToLocalChecked() 27 | 28 | 29 | #define REQ_EXT_ARG(I, VAR) \ 30 | if (info.Length() <= (I) || !info[I]->IsExternal()) \ 31 | return Nan::ThrowTypeError( \ 32 | "Argument " #I " invalid"); \ 33 | Local VAR = Local::Cast(info[I]); 34 | 35 | char * ErrorMessage(const ISC_STATUS *pvector, char *err_msg, int max_len); 36 | 37 | class Connection; 38 | class Transaction; 39 | 40 | #endif 41 | 42 | -------------------------------------------------------------------------------- /fb/include/ib_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PROGRAM: UDF and Blob filter Utilities library 3 | * MODULE: ib_util.h 4 | * DESCRIPTION: Prototype header file for ib_util.c 5 | * 6 | * The contents of this file are subject to the Interbase Public 7 | * License Version 1.0 (the "License"); you may not use this file 8 | * except in compliance with the License. You may obtain a copy 9 | * of the License at http://www.Inprise.com/IPL.html 10 | * 11 | * Software distributed under the License is distributed on an 12 | * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express 13 | * or implied. See the License for the specific language governing 14 | * rights and limitations under the License. 15 | * 16 | * The Original Code was created by Inprise Corporation 17 | * and its predecessors. Portions created by Inprise Corporation are 18 | * Copyright (C) Inprise Corporation. 19 | * 20 | * All Rights Reserved. 21 | * Contributor(s): ______________________________________. 22 | */ 23 | 24 | #ifndef _IB_UTIL_H 25 | #define _IB_UTIL_H 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | extern void *ib_util_malloc(long); 32 | 33 | #ifdef __cplusplus 34 | } /* extern "C" */ 35 | #endif 36 | 37 | #endif /* _IB_UTIL_H */ 38 | -------------------------------------------------------------------------------- /samples/testDisconnect/test-disconnect.js: -------------------------------------------------------------------------------- 1 | // Load configuration 2 | var cfg = require("./config").cfg; 3 | 4 | // Require modules 5 | var 6 | fb_binding = require("../../firebird.js"); 7 | 8 | 9 | 10 | var conn = fb_binding.createConnection(); 11 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 12 | if(conn.connected) { 13 | console.log("Connected to database"); 14 | } 15 | // var trans = conn.startNewTransactionSync(); 16 | // var res = conn.querySync("select * from rdb$relations"); 17 | // trans.rollbackSync(); 18 | //console.log("Query result 1", res) 19 | 20 | 21 | setTimeout(function() { 22 | console.log("Try again") 23 | var res = conn.query("select * from rdb$relations", function(err){ 24 | console.log("Error", err); 25 | }); 26 | /* 27 | conn.startNewTransaction(function(err, trans){ 28 | if(err) { 29 | console.log("error", err); 30 | conn.disconnect(); 31 | console.log("Connection is not ok", conn); 32 | return; 33 | } 34 | trans.rollbackSync(); 35 | console.log("Connection is ok", conn); 36 | }); 37 | */ 38 | 39 | 40 | }, 20000); 41 | 42 | -------------------------------------------------------------------------------- /tests/def/test-concurrent-fetch.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | var sys = require("util"); 9 | 10 | var zexports = {}; 11 | // Require modules 12 | var 13 | fb_binding = require("../../firebird"); 14 | 15 | 16 | exports.FetchFromSameConnection = function(test){ 17 | var num_of_fetches = 100 18 | test.expect(1 + num_of_fetches ); 19 | var conn = fb_binding.createConnection(); 20 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 21 | test.ok(conn.connected,"Connected to database"); 22 | var res = []; 23 | for(var i = 0; i target) 45 | { 46 | Nan::HandleScope scope; 47 | 48 | event_block::Init(); 49 | FBEventEmitter::Initialize(target); 50 | FBResult::Initialize(target); 51 | Connection::Initialize(target); 52 | FBblob::Initialize(target); 53 | FBStatement::Initialize (target); 54 | Transaction::Initialize(target); 55 | } 56 | 57 | NODE_MODULE(binding, init) 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | - v14 5 | - v12 6 | - v10 7 | env: 8 | - CXX=g++-5 9 | notifications: 10 | email: 11 | - xdenser@gmail.com 12 | before_install: 13 | - sudo add-apt-repository ppa:mapopa/ppa -y 14 | - sudo apt-get update -qq 15 | - sudo apt-get install -qq --force-yes firebird2.5-superclassic firebird2.5-dev 16 | - sudo sed /ENABLE_FIREBIRD_SERVER=/s/no/yes/ -i /etc/default/firebird2.5 17 | - cat /etc/default/firebird2.5 | grep ENABLE_FIREBIRD_SERVER 18 | - sudo service firebird2.5-superclassic start 19 | - sudo cat /etc/firebird/2.5/SYSDBA.password 20 | - sudo bash -c '(export FB_VER="2.5"; export FB_FLAVOUR="superclassic";source /usr/share/firebird2.5-common/functions.sh; writeNewPassword masterkey)' 21 | - sudo cat /etc/firebird/2.5/SYSDBA.password 22 | - npm install nodeunit 23 | - rm -f package-lock.json 24 | - sleep 5 25 | before_script: 26 | - cd build 27 | - mkdir test_db 28 | - cd test_db 29 | - sudo isql-fb -input ../../tests/createTestDB.sql 30 | - sudo chmod -c 0666 TEST.FDB 31 | - sudo chmod -v a+x /home/travis/build/xdenser/node-firebird-libfbclient/build/test_db 32 | - sudo chmod -v a+x /home/travis/build/xdenser/node-firebird-libfbclient/build 33 | - sudo chmod -v a+x /home/travis/build/xdenser/node-firebird-libfbclient 34 | - sudo chmod -v a+x /home/travis/build/xdenser 35 | - sudo chmod -v a+x /home/travis/build 36 | - sudo chmod -v a+x /home/travis 37 | - sudo chmod -v a+x /home 38 | - pwd 39 | - ls -l 40 | - cd ../.. 41 | - sleep 5 42 | 43 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'binding', 5 | 'sources': [ './src/fb-bindings.cc', './src/fb-bindings-blob.cc', 6 | './src/fb-bindings-fbresult.cc', 7 | './src/fb-bindings-connection.cc','./src/fb-bindings-eventblock.cc', 8 | './src/fb-bindings-fbeventemitter.cc', 9 | './src/fb-bindings-statement.cc', 10 | './src/fb-bindings-transaction.cc' ], 11 | 'include_dirs': [ 12 | '<(module_root_dir)/fb/include', 13 | " error_symbol; 17 | static Nan::Persistent result_symbol; 18 | 19 | class FBStatement:public FBResult{ 20 | public: 21 | static Nan::Persistent constructor_template; 22 | 23 | static void 24 | Initialize (v8::Local target); 25 | 26 | protected: 27 | static NAN_METHOD(New); 28 | 29 | void InstExecSync(const Nan::FunctionCallbackInfo& info, Transaction* transaction, int firstArg); 30 | static NAN_METHOD(ExecSync); 31 | static NAN_METHOD(ExecInTransSync); 32 | 33 | struct exec_request { 34 | FBStatement *statement; 35 | Transaction *trans; 36 | bool result; 37 | }; 38 | 39 | static void EIO_After_Exec(uv_work_t *req); 40 | 41 | static void EIO_Exec(uv_work_t *req); 42 | 43 | void InstExec(const Nan::FunctionCallbackInfo& info, Transaction* transaction, int firstArg); 44 | static NAN_METHOD(Exec); 45 | static NAN_METHOD(ExecInTrans); 46 | 47 | 48 | FBStatement(XSQLDA *insqlda, XSQLDA *outsqlda, isc_stmt_handle *astmtp, Connection* aconn); 49 | ~FBStatement(); 50 | 51 | private: 52 | XSQLDA *in_sqlda; 53 | int statement_type; 54 | bool retres; 55 | 56 | void finished() override {}; 57 | char cursor[50]; 58 | 59 | }; 60 | 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /tests/perf/select-prep-async.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | var fb = require('../../firebird'); 9 | var util = require('util'); 10 | 11 | var http = require('http'); 12 | var con = fb.createConnection(); 13 | con.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 14 | var stmt = con.prepareSync('select * from rdb$relations'); 15 | stmt.setMaxListeners(2000); 16 | stmt.lock = false; 17 | 18 | http.createServer(function (req, res) { 19 | res.writeHead(200, {'Content-Type': 'text/plain'}); 20 | // console.log(stmt); 21 | 22 | var resp = function(){ 23 | console.log('resp'); 24 | if(stmt.lock){ 25 | stmt.once('unlock',resp); 26 | return; 27 | } 28 | stmt.lock = true; 29 | console.log('resp start'); 30 | 31 | stmt.once('result',function(err){ 32 | var rows = []; 33 | if(err) console.log('exec error '+err.message); 34 | console.log('before fetch'); 35 | stmt.fetch("all",true,function(r){ 36 | rows.push(r); 37 | }, function(err){ 38 | if(err) console.log('error '+err.message); 39 | console.log('after fetch ' +rows.length); 40 | res.end(util.inspect(rows)); 41 | stmt.lock = false; 42 | process.nextTick(function(){ 43 | stmt.emit('unlock'); 44 | }); 45 | }); 46 | }); 47 | stmt.exec(); 48 | 49 | } 50 | 51 | 52 | resp(); 53 | 54 | }).listen(1337, "127.0.0.1"); 55 | console.log('Server running at http://127.0.0.1:1337/'); 56 | 57 | process.on('exit',function(){ 58 | con.disconnect(); 59 | }); -------------------------------------------------------------------------------- /tests/def/test-fbres.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | 9 | // Require modules 10 | var 11 | fb_binding = require("../../firebird.js"); 12 | 13 | var 14 | testCase = require("nodeunit").testCase; 15 | 16 | module.exports = testCase({ 17 | setUp: function(callback){ 18 | this.conn = fb_binding.createConnection(); 19 | this.conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 20 | this.res = this.conn.querySync("select * from rdb$relations"); 21 | callback(); 22 | }, 23 | tearDown: function(callback){ 24 | this.conn.disconnect(); 25 | callback(); 26 | }, 27 | FBResult: function(test){ 28 | test.expect(1); 29 | test.ok(this.res,"There is result"); 30 | test.done(); 31 | }, 32 | FBResultFetchSyncArgs: function(test){ 33 | test.expect(1); 34 | test.throws(function(){ 35 | this.res.fetchSync(); 36 | },"Expect Arguments"); 37 | test.done(); 38 | }, 39 | FBResFetchSyncOne: function(test){ 40 | test.expect(1); 41 | var row = this.res.fetchSync("all",false); 42 | test.ok(row, "There is row"); 43 | test.done(); 44 | }, 45 | FBResFetchMany: function(test){ 46 | var c = 0; 47 | test.expect(3); 48 | this.res.fetch(2,false, function(row){ 49 | c++; 50 | test.ok(row,"A Row"); 51 | }, function(err,eof){ 52 | test.ok(!err,"No Error"); 53 | test.done(); 54 | }); 55 | } 56 | }); 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/fb-bindings-transaction.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_FB_BINDINGS_TRANSACTION_H_ 2 | #define SRC_FB_BINDINGS_TRANSACTION_H_ 3 | 4 | 5 | #include "./fb-bindings.h" 6 | #include "./fb-bindings-fbeventemitter.h" 7 | 8 | class Transaction : public FBEventEmitter { 9 | 10 | public: 11 | 12 | Connection *connection; 13 | isc_tr_handle trans; 14 | ISC_STATUS_ARRAY status; 15 | 16 | static Nan::Persistent constructor_template; 17 | static void Initialize(v8::Local target); 18 | static NAN_METHOD(New); 19 | 20 | bool commit_transaction(); 21 | 22 | bool rollback_transaction(); 23 | 24 | bool start_transaction(); 25 | 26 | enum TransReqType { 27 | rCommit, 28 | rRollback, 29 | rStart 30 | }; 31 | 32 | struct transaction_request { 33 | Nan::Callback *callback; 34 | Transaction *transaction; 35 | TransReqType type; 36 | bool result; 37 | }; 38 | 39 | Transaction(Connection *conn); 40 | ~Transaction(); 41 | 42 | static void EIO_After_TransactionRequest(uv_work_t *req); 43 | 44 | static void EIO_TransactionRequest(uv_work_t *req); 45 | 46 | void makeTrRequest(const Nan::FunctionCallbackInfo& info, TransReqType type); 47 | 48 | char err_message[MAX_ERR_MSG_LEN]; 49 | 50 | static NAN_GETTER(InTransactionGetter); 51 | 52 | static NAN_METHOD(Commit); 53 | void InstCommit(const Nan::FunctionCallbackInfo& info); 54 | static NAN_METHOD(Rollback); 55 | void InstRollback(const Nan::FunctionCallbackInfo& info); 56 | static NAN_METHOD(Start); 57 | void InstStart(const Nan::FunctionCallbackInfo& info); 58 | 59 | static NAN_METHOD(CommitSync); 60 | static NAN_METHOD(RollbackSync); 61 | static NAN_METHOD(StartSync); 62 | 63 | static NAN_METHOD(QuerySync); 64 | static NAN_METHOD(Query); 65 | 66 | static NAN_METHOD(PrepareSync); 67 | }; 68 | #endif -------------------------------------------------------------------------------- /tests/perf/select-prep-async-trans.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | var fb = require('../../firebird'); 9 | var util = require('util'); 10 | 11 | var http = require('http'); 12 | var con = fb.createConnection(); 13 | con.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 14 | var stmt = con.prepareSync('select * from rdb$relations'); 15 | stmt.setMaxListeners(2000); 16 | stmt.lock = false; 17 | 18 | http.createServer(function (req, res) { 19 | res.writeHead(200, {'Content-Type': 'text/plain'}); 20 | // console.log(stmt); 21 | var exec = function(){ 22 | stmt.exec(); 23 | stmt.once('result',function(err){ 24 | //var rows = []; 25 | res.write('['); 26 | stmt.fetch("all",true,function(r){ 27 | // rows.push(r); 28 | res.write(JSON.stringify(r)+','); 29 | }, function(err){ 30 | res.end(']'); 31 | con.commit(function(err){ 32 | //if(err) return; 33 | stmt.lock = false; 34 | process.nextTick(function(){ 35 | stmt.emit('unlock'); 36 | }); 37 | }); 38 | }); 39 | }); 40 | }; 41 | 42 | var resp = function(){ 43 | if(stmt.lock){ 44 | stmt.once('unlock',resp); 45 | return; 46 | } 47 | stmt.lock = true; 48 | if(!con.inTransaction) con.startTransaction(function(err){ 49 | if(!err) exec(); 50 | }); 51 | else exec(); 52 | } 53 | 54 | 55 | resp(); 56 | 57 | }).listen(1337, "127.0.0.1"); 58 | console.log('Server running at http://127.0.0.1:1337/'); 59 | 60 | process.on('exit',function(){ 61 | con.disconnect(); 62 | }); -------------------------------------------------------------------------------- /tests/def/test-storedproc.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | 9 | // Require modules 10 | var 11 | fb_binding = require("../../firebird.js"); 12 | 13 | var 14 | testCase = require("nodeunit").testCase; 15 | 16 | module.exports = testCase({ 17 | setUp: function(callback){ 18 | this.conn = new fb_binding.createConnection(); 19 | this.conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 20 | var sql = 'CREATE OR ALTER PROCEDURE TESTINGPROCEDURE (TESTINPUT BIGINT) RETURNS ( TESTOUTPUT BIGINT ) AS declare variable CON integer; declare variable MAXCON integer; begin TESTOUTPUT = TESTINPUT; end'; 21 | this.conn.querySync(sql); 22 | callback(); 23 | }, 24 | tearDown: function(callback){ 25 | this.conn.disconnect(); 26 | this.conn = null; 27 | var conn = new fb_binding.createConnection(); 28 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 29 | conn.querySync('drop procedure TESTINGPROCEDURE'); 30 | conn.disconnect(); 31 | callback(); 32 | }, 33 | testProc : function(test) { 34 | test.expect(2); 35 | 36 | const query = "EXECUTE PROCEDURE TESTINGPROCEDURE (1);" 37 | 38 | this.conn.startNewTransaction((err, transaction) => { 39 | transaction.query(query, (err, result) => { 40 | if(err) { 41 | console.log(err); 42 | } 43 | test.ok(!err, "error on query"); 44 | transaction.commit(err => { 45 | //console.log(result); 46 | test.equal(result.TESTOUTPUT, 1, "wrong result"); 47 | test.done(); 48 | }) 49 | }); 50 | }); 51 | 52 | } 53 | }); -------------------------------------------------------------------------------- /src/fb-bindings-blob.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright by Denys Khanzhiyev aka xdenser 3 | * 4 | * See license text in LICENSE file 5 | */ 6 | #ifndef SRC_FB_BINDINGS_BLOB_H_ 7 | #define SRC_FB_BINDINGS_BLOB_H_ 8 | #define BUILDING_NODE_EXTENSION 1 9 | 10 | 11 | #include "./fb-bindings.h" 12 | #include 13 | #include 14 | 15 | #include "./fb-bindings-fbeventemitter.h" 16 | #include "./fb-bindings-transaction.h" 17 | 18 | 19 | 20 | class FBblob : public FBEventEmitter { 21 | 22 | public: 23 | 24 | static Nan::Persistent constructor_template; 25 | 26 | static void 27 | Initialize (v8::Local target); 28 | 29 | static bool HasInstance(v8::Local val); 30 | void getId(ISC_QUAD* Idp); 31 | 32 | 33 | protected: 34 | 35 | static NAN_METHOD(New); 36 | static NAN_METHOD(ReadSync); 37 | static NAN_METHOD(Read); 38 | static NAN_METHOD(OpenSync); 39 | static NAN_METHOD(CloseSync); 40 | static NAN_METHOD(WriteSync); 41 | static NAN_METHOD(Write); 42 | 43 | struct rw_request { 44 | Nan::Callback *callback; 45 | FBblob *blob; 46 | char* buffer; 47 | uint32_t length; 48 | int res; 49 | ISC_STATUS_ARRAY status; 50 | }; 51 | 52 | 53 | static void EIO_After_Read(uv_work_t *req); 54 | 55 | static void EIO_Read(uv_work_t *req); 56 | 57 | static void EIO_After_Write(uv_work_t *req); 58 | 59 | static void EIO_Write(uv_work_t *req); 60 | 61 | static NAN_GETTER(IsReadGetter); 62 | 63 | FBblob(ISC_QUAD *id, Transaction *trans, ISC_STATUS *status); 64 | ~FBblob(); 65 | 66 | bool open(ISC_STATUS *status); 67 | int read(ISC_STATUS *status,char *buf, unsigned short len, unsigned short* alen); 68 | bool close(ISC_STATUS *status); 69 | 70 | 71 | 72 | private: 73 | ISC_QUAD blob_id; 74 | Transaction *trans; 75 | isc_blob_handle handle; 76 | bool is_read; 77 | static char err_message[MAX_ERR_MSG_LEN]; 78 | 79 | 80 | 81 | }; 82 | 83 | 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /samples/deferred/fb-deferred.js: -------------------------------------------------------------------------------- 1 | var Deferred = require('../../tools/node-deferred/lib/deferred.js').Deferred, 2 | fb=require('../../firebird'); 3 | 4 | 5 | exports.createConnection = fb.createConnection; 6 | 7 | var Connection = fb.binding.Connection, 8 | FBResult = fb.binding.FBResult, 9 | FBStatement = fb.binding.FBStatement; 10 | 11 | 12 | 13 | function MakeDeferred(obj,meth,cb_num){ 14 | var super = obj.prototype[meth]; 15 | obj.prototype[meth] = function(){ 16 | 17 | var d = new Deferred(); 18 | var args = [].slice.call(arguments); 19 | args[cb_num] = function(){ 20 | var err = arguments[0]; 21 | var args = [].slice.call(arguments,1); 22 | if(err) d.errback(err); 23 | else d.callback(args); 24 | } 25 | super.apply(this,args); 26 | return d; 27 | } 28 | } 29 | 30 | MakeDeferred(Connection,'connect',4); 31 | MakeDeferred(Connection,'query',2); 32 | MakeDeferred(Connection,'commit',0); 33 | MakeDeferred(Connection,'rollback',0); 34 | MakeDeferred(FBResult,'fetch',3); 35 | 36 | 37 | var superExec = FBStatement.prototype.exec; 38 | FBStatement.prototype.exec = function(){ 39 | var d = new Deferred(); 40 | this.once('error', function(err){ 41 | d.errback(err); 42 | }); 43 | var self = this; 44 | this.once('result', function(){ 45 | d.callback(self); 46 | }); 47 | superExec.apply(this,arguments); 48 | return d; 49 | }; 50 | 51 | 52 | /* 53 | var superConnect = Connection.prototype.connect; 54 | Connection.prototype.connect = function(db,user,pass,role,cb){ 55 | var d = new Deferred(); 56 | superConnect.call(this,db,user,pass,role,function (err){ 57 | if(err) d.errback(err); 58 | else d.callback(); 59 | }); 60 | return d; 61 | }; 62 | 63 | var superQuery = Connection.prototype.query; 64 | Connection.prototype.query = function(sql,cb){ 65 | var d = new Deferred(); 66 | superQuery.call(this,sql,function(err,res){ 67 | if(err) obj.emit('error',err); 68 | else obj.emit('result',res); 69 | if(cb) cb(err,res); 70 | }); 71 | }; 72 | */ 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /samples/fileman/elfinder.js: -------------------------------------------------------------------------------- 1 | var url = require('url'); 2 | var crypto = require('crypto'); 3 | var sys = require('sys'); 4 | 5 | function _hash(str){ 6 | var h = crypto.createHash('md5'); 7 | h.update(str); 8 | return h.digest('hex'); 9 | } 10 | 11 | function _cwd(obj,name){ 12 | if(name=='') name = 'root'; 13 | obj.cwd = { 14 | hash: _hash(name), 15 | name: name, 16 | mime: 'directory', 17 | rel : name, 18 | size: 0, 19 | date: (new Date()).toString(), 20 | read: true, 21 | write: true, 22 | rm: false 23 | }; 24 | } 25 | 26 | function _cdc(obj,path){ 27 | obj.cdc = []; 28 | } 29 | 30 | function _tree(path){ 31 | return { 32 | hash:_hash('root'), 33 | name: 'root', 34 | read: true, 35 | write: true, 36 | dirs:[] 37 | } 38 | } 39 | 40 | function _open(qry,cb){ 41 | var obj = {}; 42 | _cwd(obj,''); 43 | _cdc(obj,''); 44 | if(('tree' in qry)&& qry.tree){ 45 | obj.tree = _tree(''); 46 | } 47 | cb(obj); 48 | } 49 | 50 | var commands = { 51 | open: _open 52 | } 53 | 54 | function extend(obj) 55 | { 56 | for(var f in obj){ 57 | if(obj.hasOwnProperty(f)) this[f] = obj[f]; 58 | } 59 | } 60 | 61 | function elfinder(req,res){ 62 | // console.log('in elfinder'); 63 | var q = url.parse(req.url,true); 64 | var command = 'open'; 65 | if('cmd' in q.query) command =q.query.cmd; 66 | 67 | var add = {}; 68 | if('init' in q.query){ 69 | // console.log(sys.inspect(req.headers)); 70 | add.disabled = []; 71 | add.params = { 72 | url : 'http://'+req.headers.host+'/node/elfinder', 73 | dotFiles: true, 74 | uplMaxSize: '15M', 75 | extract: [], 76 | archives: [] 77 | }; 78 | }; 79 | 80 | if(command in commands){ 81 | commands[command](q.query,function(obj){ 82 | res.setHeader('Content-Type','application/json'); 83 | extend.call(obj,add); 84 | res.end(JSON.stringify(obj)); 85 | }) 86 | } 87 | else 88 | { 89 | res.setHeader('Content-Type','text/html'); 90 | res.statusCode = 503; 91 | res.end('Unknown command!'); 92 | } 93 | } 94 | 95 | exports.elfinder = elfinder; -------------------------------------------------------------------------------- /tests/def/test-concurrent-query.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | 9 | var zexports = {}; 10 | // Require modules 11 | var 12 | fb_binding = require("../../firebird"); 13 | 14 | 15 | exports.TwoSmallQueries = function (test) { 16 | test.expect(6); 17 | var conn = fb_binding.createConnection(); 18 | conn.connect(cfg.db, cfg.user, cfg.password, cfg.role, function(){ 19 | test.ok(conn.connected,"Connected to database"); 20 | var queries = 0; 21 | function check(){ 22 | if(queries==2) 23 | { 24 | conn.disconnect(); 25 | test.ok(!conn.connected,"Disconnected from database"); 26 | test.done(); 27 | } 28 | }; 29 | conn.query("select * from rdb$relations", function(err,res){ 30 | test.ok(!err,"No Error"); 31 | test.ok(res,"Can query"); 32 | queries++; 33 | check(); 34 | }); 35 | conn.query("select * from rdb$relations", function(err,res){ 36 | test.ok(!err,"No Error"); 37 | test.ok(res,"Can query"); 38 | queries++; 39 | check(); 40 | }); 41 | }); 42 | } 43 | 44 | exports.AHundredOfQueries = function(test){ 45 | var query_count = 200; 46 | test.expect(query_count*2+2); 47 | var conn = fb_binding.createConnection(); 48 | conn.addListener('z',function(){}); 49 | conn.setMaxListeners(0); 50 | 51 | var queried = 0; 52 | function Finish(){ 53 | if(queried==query_count){ 54 | conn.disconnect(); 55 | test.ok(!conn.connected,"Disconnected from database"); 56 | test.done(); 57 | } 58 | } 59 | function MakeQuery(){ 60 | conn.query("select * from rdb$relations", function(err,res){ 61 | test.ok(!err,"No Error"); 62 | if(err) console.log(err.message); 63 | test.ok(res,"Can query"); 64 | queried++; 65 | Finish(); 66 | }); 67 | } 68 | conn.connect(cfg.db, cfg.user, cfg.password, cfg.role, function(){ 69 | test.ok(conn.connected,"Connected to database"); 70 | for(var i = 0; i< query_count; i++) MakeQuery(); 71 | }); 72 | } -------------------------------------------------------------------------------- /src/fb-bindings-fbresult.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright by Denys Khanzhiyev aka xdenser 3 | * 4 | * See license text in LICENSE file 5 | */ 6 | #ifndef SRC_FB_BINDINGS_FBRESULT_H_ 7 | #define SRC_FB_BINDINGS_FBRESULT_H_ 8 | 9 | 10 | #define BUILDING_NODE_EXTENSION 1 11 | #include 12 | #include 13 | #include 14 | #include 15 | //#include 16 | #include "./fb-bindings.h" 17 | #include "./fb-bindings-fbeventemitter.h" 18 | #include "./fb-bindings-blob.h" 19 | #include "./fb-bindings-connection.h" 20 | 21 | 22 | class FBResult : public FBEventEmitter { 23 | 24 | public: 25 | 26 | static Nan::Persistent constructor_template; 27 | 28 | static void 29 | Initialize (v8::Local target); 30 | 31 | // bool process_result(XSQLDA **sqldap, isc_stmt_handle *stmtp, Local res); 32 | 33 | static bool prepare_sqlda(XSQLDA *sqlda); 34 | static void clean_sqlda(XSQLDA *sqlda); 35 | static bool clone_sqlda(XSQLDA *src_sqlda,XSQLDA **dest_sqlda); 36 | static void set_params(XSQLDA *sqlda, Nan::NAN_METHOD_ARGS_TYPE info, int firstArg); 37 | Local getCurrentRow(bool asObject); 38 | 39 | protected: 40 | static NAN_METHOD(New); 41 | 42 | 43 | static Local 44 | GetFieldValue(XSQLVAR *var, Connection* conn); 45 | 46 | static NAN_METHOD(FetchSync); 47 | 48 | struct fetch_request { 49 | Nan::Callback *rowCallback; 50 | Nan::Callback *eofCallback; 51 | FBResult *res; 52 | int rowCount; 53 | ISC_STATUS fetchStat; 54 | bool rowAsObject; 55 | bool result; 56 | }; 57 | 58 | static void EIO_After_Fetch(uv_work_t *req); 59 | 60 | static void EIO_Fetch(uv_work_t *req); 61 | 62 | static NAN_METHOD(Fetch); 63 | 64 | FBResult(XSQLDA *asqldap, isc_stmt_handle *astmtp, Connection *conn); 65 | 66 | virtual ~FBResult(); 67 | 68 | ISC_STATUS_ARRAY status; 69 | char err_message[MAX_ERR_MSG_LEN]; 70 | XSQLDA *sqldap; 71 | isc_stmt_handle stmt; 72 | Connection *connection; 73 | static Nan::AsyncResource asyncResource; 74 | 75 | private: 76 | static const double dscales[19]; 77 | virtual void finished(); 78 | void releaseStatement(); 79 | 80 | }; 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /src/fb-bindings-fbeventemitter.cc: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright by Denys Khanzhiyev aka xdenser 3 | * 4 | * See license text in LICENSE file 5 | */ 6 | #define BUILDING_NODE_EXTENSION 1 7 | #include "./fb-bindings-fbeventemitter.h" 8 | 9 | Nan::Persistent FBEventEmitter::constructor_template; 10 | 11 | Nan::AsyncResource FBEventEmitter::asyncResource("FBEventEmitter"); 12 | 13 | void FBEventEmitter::Initialize(v8::Local target) 14 | { 15 | 16 | Local t = Nan::New(); 17 | 18 | constructor_template.Reset(t); 19 | t->SetClassName(Nan::New("FBEventEmitter").ToLocalChecked()); 20 | 21 | Nan::Set(target, Nan::New("FBEventEmitter").ToLocalChecked(), Nan::GetFunction(t).ToLocalChecked()); 22 | } 23 | 24 | 25 | void FBEventEmitter::Emit(Local event, int argc, Local argv[]) 26 | { 27 | if(!persistent().IsEmpty()) { 28 | Nan::HandleScope scope; 29 | Local argv1[11]; 30 | if(argc>10) Nan::ThrowError("Cant process more than 10 arguments"); 31 | argv1[0] = event; 32 | for(int i=0;ihandle(), Nan::New("emit").ToLocalChecked(), argc+1, argv1); 34 | } 35 | } 36 | 37 | void FBEventEmitter::start_async() 38 | { 39 | Nan::HandleScope scope; 40 | in_async = true; 41 | Emit(Nan::New("fbStartAsync").ToLocalChecked(), 0, NULL); 42 | } 43 | 44 | void FBEventEmitter::stop_async() 45 | { 46 | Nan::HandleScope scope; 47 | in_async = false; 48 | Emit(Nan::New("fbStopAsync").ToLocalChecked(), 0, NULL); 49 | } 50 | 51 | FBEventEmitter::FBEventEmitter () : Nan::ObjectWrap () 52 | { 53 | in_async = false; 54 | // Handle argv[1]; 55 | // printf("in init\n"); 56 | // node::MakeCallback(handle_,"init",0,argv); 57 | } 58 | 59 | /*Handle 60 | FBEventEmitter::InAsyncGetter(Local property, 61 | const AccessorInfo &info) */ 62 | 63 | NAN_GETTER(FBEventEmitter::InAsyncGetter) 64 | { 65 | Nan::HandleScope scope; 66 | FBEventEmitter *fbee = Nan::ObjectWrap::Unwrap(info.This()); 67 | info.GetReturnValue().Set(Nan::New(fbee->in_async)); 68 | } 69 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # http://www.appveyor.com/docs/appveyor-yml 2 | 3 | # Test against these versions of Io.js and Node.js. 4 | environment: 5 | fb_download: https://github.com/xdenser/files/blob/master/fb25.7z?raw=true 6 | fb_start: .\bin\fb_inet_server.exe -a -m 7 | matrix: 8 | # lts 9 | - nodejs_version: "14" 10 | - nodejs_version: "16" 11 | # stable 12 | - nodejs_version: "17" 13 | 14 | # Install scripts. (runs after repo cloning) 15 | install: 16 | # Get the latest stable version of Node 0.STABLE.latest 17 | - ps: if($env:nodejs_version -ne "0.8") {Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version)} 18 | - IF NOT %nodejs_version% == 1 npm -g install npm 19 | - IF NOT %nodejs_version% == 1 set PATH=%APPDATA%\npm;%PATH% 20 | # Typical npm stuff. 21 | - npm install 22 | - npm install nodeunit 23 | #- IF %nodejs_version% == 0.8 node node_modules\node-gyp\bin\node-gyp.js rebuild --directory test 24 | #- IF NOT %nodejs_version% == 0.8 npm run rebuild-tests 25 | 26 | # Post-install test scripts. 27 | test_script: 28 | # Output useful info for debugging. 29 | - node --version 30 | - npm --version 31 | - ps: $sourceDir = Resolve-Path . 32 | - ps: $startTest = "$sourceDir\node_modules\.bin\nodeunit.cmd tests/def/" 33 | #- ps: iex $startTest 34 | - .\node_modules\.bin\nodeunit.cmd tests/def/" 35 | # run tests 36 | #- IF NOT %nodejs_version% == 1 npm test 37 | #- IF %nodejs_version% == 1 iojs node_modules\tap\bin\tap.js --gc test\js\*-test.js 38 | 39 | # Don't actually build. 40 | build: off 41 | 42 | before_test: 43 | - ps: $sourceDir = Resolve-Path . 44 | - ps: $testdbDir = "$sourceDir\build\test_db" 45 | - ps: $createDb = "$sourceDir\tests\createTestDB.sql" 46 | - ps: $isql = "c:\firebird\server\bin\isql.exe -input $createDb" 47 | 48 | - ps: mkdir C:\firebird | Out-Null 49 | - ps: cd C:\firebird 50 | - ps: Start-FileDownload "$env:fb_download" | Out-Null 51 | - ps: 7z x ($env:fb_download -replace '.+/([^/]+)\?raw=true','$1') 52 | 53 | - ps: ls C:\firebird\server 54 | - ps: iex "C:\firebird\server\$env:fb_start" 55 | - ps: sleep -s 5 56 | - ps: ni firebird.log -ItemType File | Out-Null 57 | - ps: mkdir $testdbDir 58 | - ps: cd $testdbDir 59 | - ps: iex $isql 60 | - ps: ls $testdbDir 61 | - ps: cd $sourceDir 62 | - ps: cp c:\firebird\server\bin\fbclient.dll $sourceDir\build\Release\ 63 | 64 | 65 | # Set build version format here instead of in the admin panel. 66 | version: "{build}" 67 | -------------------------------------------------------------------------------- /tests/def/test-transaction.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | 9 | // Require modules 10 | var 11 | fb_binding = require("../../firebird.js"); 12 | var cfg = require("../config").cfg; 13 | 14 | 15 | exports.setUp = function (callback) { 16 | this.conn = new fb_binding.createConnection(); 17 | this.conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 18 | this.conn.querySync('create table TRANS_TEST_TABLE ( test_field1 INT, test_field2 VARCHAR(10))'); 19 | callback(); 20 | } 21 | 22 | exports.tearDown = function (callback) { 23 | this.conn.disconnect(); 24 | this.conn = null; 25 | var conn = new fb_binding.createConnection(); 26 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 27 | conn.querySync('drop table TRANS_TEST_TABLE'); 28 | conn.disconnect(); 29 | callback(); 30 | } 31 | 32 | exports.testNewTransactionSync = function (test) { 33 | test.expect(2); 34 | try { 35 | var trans = this.conn.startNewTransactionSync(); 36 | trans.querySync("insert into TRANS_TEST_TABLE (test_field1, test_field2) values (0,'some') "); 37 | var r1 = this.conn.querySync("select count(*) from TRANS_TEST_TABLE").fetchSync('all', false); 38 | test.equal(r1[0][0], 0, "Zero expected"); 39 | trans.commitSync(); 40 | this.conn.rollbackSync(); 41 | var r2 = this.conn.querySync("select count(*) from TRANS_TEST_TABLE").fetchSync('all', false); 42 | test.equal(r2[0][0], 1, "One expected"); 43 | test.done(); 44 | } catch(e) { 45 | console.log("error", e); 46 | } 47 | } 48 | 49 | function wr(f) { 50 | return function () { 51 | try { 52 | f.apply(this, arguments); 53 | } catch (e) { 54 | console.log("Error in callback function ", f, e); 55 | }; 56 | } 57 | } 58 | 59 | exports.testNewTransactionAsync = function (test) { 60 | test.expect(4); 61 | var conn = this.conn; 62 | conn.startNewTransaction(wr(function (err, tr) { 63 | test.equal(err, null, "Error starting transaction"); 64 | tr.querySync("insert into TRANS_TEST_TABLE (test_field1, test_field2) values (0,'some') "); 65 | var r1 = conn.querySync("select count(*) from TRANS_TEST_TABLE").fetchSync('all', false); 66 | test.equal(r1[0][0], 0, "Zero expected"); 67 | tr.commit(wr(function (err) { 68 | test.equal(err, null, "Error commiting transaction"); 69 | conn.rollbackSync(); 70 | var r2 = conn.querySync("select count(*) from TRANS_TEST_TABLE").fetchSync('all', false); 71 | test.equal(r2[0][0], 1, "One expected"); 72 | test.done(); 73 | })); 74 | })); 75 | }; 76 | 77 | -------------------------------------------------------------------------------- /tests/perf/select-pool-async.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | var fb = require('../../firebird'); 9 | var util = require('util'); 10 | var events = require('events'); 11 | 12 | var http = require('http'); 13 | 14 | function ConnectionPool() 15 | { 16 | events.EventEmitter.call(this); 17 | this.conns = []; 18 | this.busy = []; 19 | this.MaxConns = 5; 20 | this.newConn = function(cb){ 21 | var c = fb.createConnection(); 22 | c.connect(cfg.db, cfg.user, cfg.password, cfg.role,function(){ 23 | this.conns.push(c); 24 | cb(); 25 | }); 26 | }; 27 | this.get = function(cb) 28 | { 29 | var self = this; 30 | var c = this.conns.pop(); 31 | if(c) { 32 | this.busy.push(c); 33 | cb(c); 34 | } 35 | else 36 | if((this.busy.length) >= this.MaxConns){ 37 | this.once('release',function(){ 38 | self.get(cb); 39 | }); 40 | } 41 | else { 42 | this.newConn(function(){ 43 | this.get(cb); 44 | }); 45 | } 46 | }; 47 | 48 | this.release = function(con){ 49 | for(var i=0;i 12 | #include 13 | #include 14 | //#include 15 | #include 16 | #include "./fb-bindings.h" 17 | 18 | //#define DEBUG 19 | 20 | #if (NODE_MODULE_VERSION > 0x000B) 21 | #define _UV_NOTIFICATION_SIGNATURE (uv_async_s *w) 22 | #endif 23 | 24 | #if (NODE_MODULE_VERSION < 0x000C) 25 | #define _UV_NOTIFICATION_SIGNATURE (uv_async_t *w, int revents) 26 | #endif 27 | 28 | #define MAX_EVENTS_PER_BLOCK 15 29 | 30 | using namespace node; 31 | using namespace v8; 32 | 33 | /* 34 | * Class event_block 35 | * Firebird accepts events in blocks by 15 event names. 36 | * This class helps to organize linked list chain of such blocks 37 | * to support "infinite" number of events. 38 | */ 39 | typedef char* ev_names[MAX_EVENTS_PER_BLOCK]; 40 | static Nan::Persistent fbevent_symbol; 41 | 42 | class event_block { 43 | 44 | public: 45 | Connection *conn; 46 | ISC_UCHAR *event_buffer; 47 | ISC_UCHAR *result_buffer; 48 | ISC_LONG event_id; 49 | ev_names event_names; 50 | int count; 51 | short blength; 52 | event_block *next; 53 | event_block *prev; 54 | uv_async_t *event_; 55 | 56 | 57 | ISC_STATUS_ARRAY status; 58 | char err_message[MAX_ERR_MSG_LEN]; 59 | isc_db_handle *db; 60 | bool traped, queued; 61 | 62 | static void Init(); 63 | 64 | #ifdef DEBUG 65 | static void dump_buf(char* buf, int len); 66 | #endif 67 | 68 | // calculates event counts 69 | // as difference between result_buffer and event_buffer 70 | // places result in Vector 71 | void get_counts(ISC_STATUS* Vector); 72 | 73 | // queue event_buffer to trap event 74 | bool queue(); 75 | 76 | // cancel previously queued trap 77 | bool cancel(); 78 | 79 | static void isc_ev_callback(void *aeb, ISC_USHORT length, const ISC_UCHAR *updated); 80 | 81 | // this is event nitification proc 82 | // here we emit node events 83 | static void event_notification _UV_NOTIFICATION_SIGNATURE; 84 | 85 | static void que_event(event_block *eb); 86 | 87 | int hasEvent(char *Event); 88 | 89 | bool addEvent(char *Event); 90 | 91 | void removeEvent(char *Event); 92 | 93 | static void 94 | RegEvent(event_block** rootp, char *Event, Connection *aconn, isc_db_handle *db); 95 | 96 | static event_block* FindBlock(event_block* root, char *Event); 97 | 98 | static void RemoveEvent(event_block** root, char *Event); 99 | 100 | event_block(Connection *aconn,isc_db_handle *adb); 101 | 102 | ~event_block(); 103 | }; 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /tests/def/test-async.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | 9 | // Require modules 10 | var 11 | fb_binding = require("../../firebird.js"); 12 | 13 | 14 | exports.AsyncConnection = function (test) { 15 | test.expect(5); 16 | var conn = fb_binding.createConnection(); 17 | conn.connect(cfg.db, cfg.user, cfg.password, cfg.role, function(cerr){ 18 | test.ok(!cerr,"No Error"); 19 | if(cerr) { 20 | console.error(cerr); 21 | return; 22 | } 23 | test.ok(conn.connected,"Connected to database"); 24 | conn.query("select * from rdb$relations", function(err,res){ 25 | test.ok(!err,"No Error"); 26 | test.ok(res,"Can query"); 27 | conn.disconnect(); 28 | test.ok(!conn.connected,"Disconnected to database"); 29 | test.done(); 30 | }); 31 | }); 32 | }; 33 | 34 | exports.AsyncQueryWithError = function (test) { 35 | test.expect(5); 36 | var conn = fb_binding.createConnection(); 37 | conn.connect(cfg.db, cfg.user, cfg.password, cfg.role, function(){ 38 | test.ok(conn.connected,"Connected to database"); 39 | conn.on('error',function(err){ 40 | test.ok(err,"There is error"); 41 | return true; 42 | }); 43 | conn.query("select * from non_existent_table", function(err,res){ 44 | test.ok(err,"There is error"); 45 | test.ok(!res,"No result"); 46 | conn.disconnect(); 47 | test.ok(!conn.connected,"Disconnected from database"); 48 | test.done(); 49 | }); 50 | }); 51 | } 52 | 53 | exports.AsyncFetch = function(test){ 54 | test.expect(6); 55 | var conn = fb_binding.createConnection(); 56 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 57 | test.ok(conn.connected,"Connected to database"); 58 | var res = conn.querySync("select * from rdb$relations"); 59 | test.ok(res,"There is result"); 60 | res.fetch(1,true, function(row){ 61 | test.ok(row,"There is row"); 62 | test.ok(row instanceof Object,"is object"); 63 | test.ok(!(row instanceof Array),"not array"); 64 | conn.disconnect(); 65 | }, function(err,eof){ 66 | test.ok(!err, "No error!"); 67 | test.done(); 68 | }); 69 | } 70 | 71 | exports.InAsyncCallConnection = function(test){ 72 | test.expect(7); 73 | var conn = fb_binding.createConnection(); 74 | conn.on("fbStartAsync",function(){ 75 | test.ok(true,"StartAsync Called"); 76 | }); 77 | conn.on("fbStopAsync",function(){ 78 | test.ok(true,"StopAsync Called"); 79 | }); 80 | 81 | test.ok(!conn.inAsyncCall,"not inAsyncCall initially"); 82 | conn.connect(cfg.db, cfg.user, cfg.password, cfg.role, function(res){ 83 | conn.query("select * from rdb$relations", function(err,res){ 84 | conn.disconnect(); 85 | conn.once("fbStopAsync",function(){ 86 | test.done(); 87 | }); 88 | }); 89 | test.ok(conn.inAsyncCall,"inAsyncCall query"); 90 | }); 91 | test.ok(conn.inAsyncCall,"inAsyncCall connect"); 92 | } 93 | 94 | 95 | -------------------------------------------------------------------------------- /tests/perf/select-prep-async-pool.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | var fb = require('../../firebird'); 9 | var util = require('util'); 10 | var events = require('events'); 11 | 12 | var http = require('http'); 13 | 14 | function StatementPool() 15 | { 16 | events.EventEmitter.call(this); 17 | this.conns = []; 18 | this.busy = []; 19 | this.MaxConns = 5; 20 | this.newConn = function(cb){ 21 | var c ={ 22 | conn: fb.createConnection() 23 | }, self = this; 24 | 25 | c.conn.connect(cfg.db, cfg.user, cfg.password, cfg.role, function(){ 26 | c.stmt = c.conn.prepareSync('select * from rdb$relations'); 27 | // c.stmt = c.conn.prepareSync('select first 5 * from test_t where pid = ?'); 28 | self.conns.push(c); 29 | cb(); 30 | }); 31 | }; 32 | this.get = function(cb) 33 | { 34 | var self = this; 35 | var c = this.conns.pop(); 36 | if(c) { 37 | this.busy.push(c); 38 | cb(c); 39 | } 40 | else 41 | if((this.busy.length) >=this.MaxConns){ 42 | this.once('release',function(){ 43 | self.get(cb); 44 | }); 45 | } 46 | else { 47 | this.newConn(function(){ 48 | this.get(cb); 49 | }); 50 | } 51 | }; 52 | this.release = function(con){ 53 | for(var i=0;i 36 | #endif 37 | 38 | #ifdef HAVE_TIMES 39 | #include 40 | #include 41 | #else 42 | #include 43 | 44 | #ifdef __cplusplus 45 | extern "C" { 46 | #endif 47 | 48 | /* 49 | * Structure returned by times() 50 | */ 51 | struct tms 52 | { 53 | time_t tms_utime; /* user time */ 54 | time_t tms_stime; /* system time */ 55 | time_t tms_cutime; /* user time, children */ 56 | time_t tms_cstime; /* system time, children */ 57 | }; 58 | 59 | #ifdef __cplusplus 60 | } /* extern "C" */ 61 | #endif 62 | 63 | #endif /* !HAVE_TIMES */ 64 | 65 | #ifdef __cplusplus 66 | extern "C" { 67 | #endif 68 | 69 | typedef struct perf 70 | { 71 | long perf_fetches; 72 | long perf_marks; 73 | long perf_reads; 74 | long perf_writes; 75 | long perf_current_memory; 76 | long perf_max_memory; 77 | long perf_buffers; 78 | long perf_page_size; 79 | long perf_elapsed; 80 | struct tms perf_times; 81 | } PERF; 82 | 83 | typedef struct perf64 84 | { 85 | ISC_INT64 perf_fetches; 86 | ISC_INT64 perf_marks; 87 | ISC_INT64 perf_reads; 88 | ISC_INT64 perf_writes; 89 | ISC_INT64 perf_current_memory; 90 | ISC_INT64 perf_max_memory; 91 | long perf_buffers; 92 | long perf_page_size; 93 | long perf_elapsed; 94 | struct tms perf_times; 95 | } PERF64; 96 | 97 | /* Letter codes controlling printing of statistics: 98 | 99 | !f - fetches 100 | !m - marks 101 | !r - reads 102 | !w - writes 103 | !e - elapsed time (in seconds) 104 | !u - user times 105 | !s - system time 106 | !p - page size 107 | !b - number buffers 108 | !d - delta memory 109 | !c - current memory 110 | !x - max memory 111 | 112 | */ 113 | 114 | #ifdef __cplusplus 115 | } /* extern "C" */ 116 | #endif 117 | 118 | 119 | #include "../jrd/perf_proto.h" 120 | 121 | #endif /* JRD_PERF_H */ 122 | 123 | -------------------------------------------------------------------------------- /tests/def/test-blob-stream.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | 7 | // Load configuration 8 | var cfg = require("../config").cfg; 9 | 10 | // Require modules 11 | var 12 | fb_binding = require("../../firebird"); 13 | var 14 | testCase = require("nodeunit").testCase; 15 | var 16 | fs = require('fs'); 17 | var 18 | sys = require("util"); 19 | 20 | 21 | var util = { 22 | createTestTable: function(datatype){ 23 | this.conn.querySync('create table BLOB_TEST_TABLE ( test_field '+datatype+')'); 24 | }, 25 | quote:function(val){ return "'"+val+"'";} 26 | }; 27 | 28 | 29 | module.exports = testCase({ 30 | setUp: function(callback){ 31 | this.conn = new fb_binding.createConnection(); 32 | this.conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 33 | callback(); 34 | }, 35 | tearDown: function(callback){ 36 | this.conn.disconnect(); 37 | this.conn = null; 38 | var conn = new fb_binding.createConnection(); 39 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 40 | conn.querySync('drop table BLOB_TEST_TABLE'); 41 | conn.disconnect(); 42 | callback(); 43 | }, 44 | fileInsert: function(test){ 45 | test.expect(2); 46 | util.createTestTable.call(this,'BLOB SUB_TYPE 0'); 47 | 48 | var conn = this.conn; 49 | function insertBlob(cb) { 50 | var stmt = conn.prepareSync('insert into BLOB_TEST_TABLE ( test_field ) values (?)'); 51 | var blob = conn.newBlobSync(); 52 | var strm = new fb_binding.Stream(blob); 53 | test.ok(strm, 'There is a stream'); 54 | var fstrm = fs.createReadStream('tests/testdata/image.gif'); 55 | fstrm.resume(); 56 | fstrm.pipe(strm); 57 | 58 | 59 | strm.on("close", function () { 60 | stmt.execSync(strm._blob); 61 | conn.commitSync(); 62 | cb(); 63 | }); 64 | strm.on('error', function (err) { 65 | console.log('error in write blob stream ',err); 66 | }) 67 | } 68 | 69 | 70 | function checkBlob() { 71 | var res = conn.querySync("select * from BLOB_TEST_TABLE").fetchSync("all", false)[0][0]; 72 | 73 | var strm = new fb_binding.Stream(res); 74 | var buffers = []; 75 | strm.on('data', function (data) { 76 | buffers.push(data); 77 | }) 78 | strm.on('end', function () { 79 | var buf = Buffer.concat(buffers); 80 | var expected = fs.readFileSync('tests/testdata/image.gif'); 81 | test.deepEqual(buf, expected, "Buffers not equal"); 82 | test.done(); 83 | }); 84 | 85 | strm.resume(); 86 | } 87 | 88 | insertBlob(checkBlob); 89 | 90 | } 91 | }); 92 | -------------------------------------------------------------------------------- /tests/def/test-blob.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | 7 | // Load configuration 8 | var cfg = require("../config").cfg; 9 | 10 | // Require modules 11 | var 12 | fb_binding = require("../../firebird"); 13 | var 14 | testCase = require("nodeunit").testCase; 15 | 16 | 17 | var util = { 18 | getDataTypeResult: function(datatype,value){ 19 | this.conn.querySync('create table BLOB_TEST_TABLE ( test_field '+datatype+')'); 20 | this.conn.querySync('insert into BLOB_TEST_TABLE (test_field) values ('+value+')'); 21 | this.conn.commitSync(); 22 | return this.conn.querySync('select first 1 test_field from BLOB_TEST_TABLE').fetchSync("all",false)[0][0]; 23 | }, 24 | quote:function(val){ return "'"+val+"'";} 25 | }; 26 | 27 | 28 | module.exports = testCase({ 29 | setUp: function(callback){ 30 | this.conn = new fb_binding.createConnection(); 31 | this.conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 32 | callback(); 33 | }, 34 | tearDown: function(callback){ 35 | this.conn.disconnect(); 36 | this.conn = null; 37 | var conn = new fb_binding.createConnection(); 38 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 39 | conn.querySync('drop table BLOB_TEST_TABLE'); 40 | conn.disconnect(); 41 | callback(); 42 | }, 43 | textBlob: function(test){ 44 | test.expect(1); 45 | Data = 'some data in blob'; 46 | var res = util.getDataTypeResult.call(this,'BLOB SUB_TYPE 1',util.quote(Data)); 47 | var buf = new Buffer(1024); 48 | res._openSync(); 49 | var len = res._readSync(buf); 50 | res._closeSync(); 51 | test.equal(buf.toString('utf8',0,len),Data,'blob '); 52 | test.done(); 53 | }, 54 | asyncRead: function(test){ 55 | test.expect(2); 56 | //Data = 'some data for async blob'; 57 | chars = '0123456789abcdefgh'; 58 | for(var i=0; i < 2000; i++) Data+=chars.charAt(Math.floor(Math.random()*chars.length)); 59 | 60 | var res = util.getDataTypeResult.call(this,'BLOB SUB_TYPE 1',util.quote(Data)); 61 | var buf = new Buffer(2048); 62 | res._openSync(); 63 | res._read(buf, function (err, b, isLast) { 64 | test.ifError(err); 65 | res._closeSync(); 66 | test.equal(b.toString('utf8'), Data, 'blob '); 67 | 68 | test.done(); 69 | }); 70 | }, 71 | readAll:function(test){ 72 | test.expect(2); 73 | Data = ''; 74 | chars = '0123456789abcdefgh'; 75 | for(var i=0; i < 10000; i++) Data+=chars.charAt(Math.floor(Math.random()*chars.length)); 76 | var res = util.getDataTypeResult.call(this,'BLOB SUB_TYPE 1',util.quote(Data)); 77 | res._readAll(0,function(err,res,len){ 78 | test.ifError(err); 79 | test.equal(res.toString('utf8',0,len),Data,'equal Data'); 80 | test.done(); 81 | }); 82 | } 83 | }); 84 | -------------------------------------------------------------------------------- /tests/def/test-sync.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | 9 | // Require modules 10 | var 11 | fb_binding = require("../../firebird.js"); 12 | 13 | 14 | 15 | exports.SyncConnection = function (test) { 16 | test.expect(3); 17 | var conn = fb_binding.createConnection(); 18 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 19 | test.ok(conn.connected,"Connected to database"); 20 | var res = conn.querySync("select * from rdb$relations"); 21 | test.ok(res,"Can query"); 22 | conn.disconnect(); 23 | test.ok(!conn.connected,"Disconnected to database"); 24 | test.done(); 25 | }; 26 | 27 | function Connect(){ 28 | var conn = fb_binding.createConnection(); 29 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 30 | conn.querySync("create table TEST_TRANS (ID INTEGER, NAME VARCHAR(20))"); 31 | var sql = 'create or alter procedure TEST_PROC(\n'+ 32 | '"INPUT" varchar(25))\n'+ 33 | 'returns (\n'+ 34 | '"OUTPUT" varchar(25))\n'+ 35 | 'as\n'+ 36 | 'BEGIN\n'+ 37 | ' output = input;\n'+ 38 | ' suspend;\n'+ 39 | 'END\n'; 40 | conn.querySync(sql); 41 | return conn; 42 | } 43 | function Close(conn){ 44 | conn.disconnect(); 45 | var con = fb_binding.createConnection(); 46 | con.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 47 | con.querySync("drop table TEST_TRANS;"); 48 | con.querySync('drop procedure TEST_PROC'); 49 | con.disconnect(); 50 | } 51 | 52 | exports.SyncDDLTransaction = function(test) { 53 | test.expect(2); 54 | var conn = Connect(); 55 | test.ok(conn.connected,"Connected to database"); 56 | test.ok(!conn.inTransaction,"DDL should commit automatically"); 57 | Close(conn); 58 | test.done(); 59 | } 60 | 61 | exports.SyncTransCommit = function(test) { 62 | test.expect(5); 63 | var conn = Connect(); 64 | test.ok(conn.connected,"Connected to database"); 65 | 66 | test.doesNotThrow(function(){ 67 | conn.querySync("insert into TEST_TRANS (ID, NAME) values (0, 'Test Name 1')"); 68 | },"insert query"); 69 | 70 | test.ok(conn.inTransaction,"DML should not commit automatically"); 71 | 72 | test.doesNotThrow(function(){ 73 | conn.commitSync(); 74 | },"commit insert"); 75 | 76 | test.ok(!conn.inTransaction,"Not in transaction"); 77 | Close(conn); 78 | test.done(); 79 | } 80 | 81 | exports.SyncTransRollback = function(test) { 82 | test.expect(6); 83 | var conn = Connect(); 84 | test.ok(conn.connected,"Connected to database"); 85 | 86 | test.doesNotThrow(function(){ 87 | conn.querySync("insert into TEST_TRANS (ID, NAME) values (0, 'Test Name 1')"); 88 | },"insert query"); 89 | 90 | var res; 91 | test.doesNotThrow(function(){ 92 | res = conn.querySync("select * from TEST_TRANS").fetchSync("all",true); 93 | },"select before rollback"); 94 | 95 | test.ok(res.length == 1, "Got one result"); 96 | 97 | conn.rollbackSync(); 98 | 99 | var res; 100 | test.doesNotThrow(function(){ 101 | res = conn.querySync("select * from TEST_TRANS").fetchSync("all",true); 102 | },"select after rollback"); 103 | 104 | test.ok(res.length == 0, "Got zero result"); 105 | 106 | Close(conn); 107 | test.done(); 108 | } 109 | 110 | 111 | 112 | exports.SyncQueryFromSP = function(test) { 113 | test.expect(4); 114 | var conn = Connect(); 115 | test.ok(conn.connected,"Connected to database"); 116 | var res; 117 | test.doesNotThrow(function(){ 118 | res = conn.querySync("select * from TEST_PROC('TEST STRING')").fetchSync("all",true); 119 | },"select from store proc"); 120 | test.ok(res.length == 1, "Got one result"); 121 | test.equal(res[0].OUTPUT, 'TEST STRING','returned data equal'); 122 | Close(conn); 123 | test.done(); 124 | } 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/fb-bindings-connection.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright by Denys Khanzhiyev aka xdenser 3 | * 4 | * See license text in LICENSE file 5 | */ 6 | #ifndef SRC_FB_BINDINGS_CONNECTION_H_ 7 | #define SRC_FB_BINDINGS_CONNECTION_H_ 8 | #define BUILDING_NODE_EXTENSION 1 9 | 10 | #define MAX_USERNAME_LENGTH 31 11 | #define MAX_ROLENAME_LENGTH 31 12 | #define MAX_PASSWORD_LENGTH 20 13 | #define PROP_LC_CTYPE "lc_ctype" 14 | #define PROP_LC_CTYPE_DECODE "lc_ctype_decode" 15 | 16 | #include 17 | #include "./fb-bindings.h" 18 | #include "./fb-bindings-fbeventemitter.h" 19 | #include "./fb-bindings-eventblock.h" 20 | #include "./fb-bindings-blob.h" 21 | #include "./fb-bindings-transaction.h" 22 | #include 23 | #include 24 | 25 | using namespace v8; 26 | using namespace node; 27 | 28 | class Connection : public FBEventEmitter { 29 | 30 | public: 31 | isc_db_handle db; 32 | Transaction* def_trans; 33 | char *lc_type = const_cast("UTF8"); 34 | bool isUTF8lctype = true; 35 | 36 | static void 37 | Initialize (v8::Local target); 38 | 39 | bool Connect (const char* Database,const char* User,const char* Password,const char* Role, const char* lc_type); 40 | 41 | bool Close(); 42 | 43 | bool process_statement(XSQLDA **sqldap, char *query, isc_stmt_handle *stmtp, int *statement_type, Transaction *atr); 44 | 45 | bool prepare_statement(XSQLDA **insqlda, XSQLDA **outsqlda, char *query, isc_stmt_handle *stmtp, Transaction *atr); 46 | 47 | bool check_trans(Transaction **tr); 48 | 49 | void doref(); 50 | 51 | void dounref(); 52 | 53 | isc_db_handle get_DB_Handle(); 54 | isc_tr_handle get_Def_Tr_Handle(); 55 | 56 | void InstQuerySync(const Nan::FunctionCallbackInfo& info, Transaction* transaction); 57 | void InstQuery(const Nan::FunctionCallbackInfo& info, Transaction* transaction); 58 | void InstPrepareSync(const Nan::FunctionCallbackInfo& info, Transaction* transaction); 59 | 60 | protected: 61 | 62 | static NAN_METHOD(New); 63 | static NAN_METHOD(ConnectSync); 64 | 65 | struct connect_request { 66 | Nan::Callback *callback; 67 | Connection *conn; 68 | Nan::Utf8String *Database; 69 | Nan::Utf8String *User; 70 | Nan::Utf8String *Password; 71 | Nan::Utf8String *Role; 72 | Nan::Utf8String *lc_type; 73 | bool res; 74 | }; 75 | 76 | static void EIO_After_Connect(uv_work_t *req); 77 | 78 | static void EIO_Connect(uv_work_t *req); 79 | 80 | static NAN_METHOD(Connect); 81 | static NAN_METHOD(Disconnect); 82 | 83 | static NAN_GETTER(ConnectedGetter); 84 | static NAN_GETTER(InTransactionGetter); 85 | 86 | static NAN_METHOD(CommitSync); 87 | static NAN_METHOD(RollbackSync); 88 | static NAN_METHOD(StartSync); 89 | 90 | static NAN_METHOD(Commit); 91 | static NAN_METHOD(Rollback); 92 | static NAN_METHOD(Start); 93 | 94 | 95 | static NAN_METHOD(QuerySync); 96 | 97 | static NAN_METHOD(StartNewTransSync); 98 | static NAN_METHOD(StartNewTrans); 99 | 100 | struct query_request { 101 | Nan::Callback *callback; 102 | Connection *conn; 103 | Transaction* transaction; 104 | Nan::Utf8String *Query; 105 | XSQLDA *sqlda; 106 | isc_stmt_handle stmt; 107 | int statement_type; 108 | bool result; 109 | }; 110 | 111 | static void EIO_After_Query(uv_work_t *req); 112 | 113 | static void EIO_Query(uv_work_t *req); 114 | 115 | 116 | static NAN_METHOD(Query); 117 | 118 | static NAN_METHOD(addEvent); 119 | static NAN_METHOD(deleteEvent); 120 | static NAN_METHOD(PrepareSync); 121 | static NAN_METHOD(NewBlobSync); 122 | 123 | static time_t get_gmt_delta(); 124 | 125 | Connection (); 126 | 127 | ~Connection (); 128 | 129 | private: 130 | 131 | ISC_STATUS_ARRAY status; 132 | event_block* fb_events; 133 | bool connected; 134 | 135 | // bool in_async; 136 | char err_message[MAX_ERR_MSG_LEN]; 137 | 138 | 139 | }; 140 | 141 | #endif 142 | -------------------------------------------------------------------------------- /tests/def/test-binding.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | 9 | // Require modules 10 | var 11 | safe_con = require("../../firebird.js"), 12 | fb_binding = safe_con.binding; 13 | 14 | exports.ConnectionBinding = function (test) { 15 | test.expect(23); 16 | test.ok("Connection" in fb_binding, "Connection"); 17 | var conn = new fb_binding.Connection; 18 | test.ok(conn, "Connection created"); 19 | test.ok("connectSync" in conn, "connectSync"); 20 | test.ok("connect" in conn, "connect"); 21 | test.ok("connected" in conn, "connected"); 22 | test.ok("disconnect" in conn, "disconnect"); 23 | test.ok("querySync" in conn, "querySync"); 24 | test.ok("query" in conn, "query"); 25 | test.ok("addFBevent" in conn,"addFBevent"); 26 | test.ok("deleteFBevent" in conn,"deleteFBevent"); 27 | test.ok("commit" in conn, "commit"); 28 | test.ok("commitSync" in conn, "commitSync"); 29 | test.ok("inTransaction" in conn, "inTransaction"); 30 | test.ok("rollback" in conn, "rollback"); 31 | test.ok("rollbackSync" in conn, "rollbackSync"); 32 | test.ok("prepareSync" in conn, "prepareSync"); 33 | test.ok("inAsyncCall" in conn, "inAsyncCall"); 34 | test.ok("startTransactionSync" in conn, "startTransactionSync"); 35 | test.ok("newBlobSync" in conn, "newBlobSync"); 36 | test.ok("startNewTransactionSync" in conn, "startNewTransactionSync"); 37 | test.ok("startNewTransaction" in conn, "startNewTransaction"); 38 | test.ok("startTransactionSync" in conn, "startTransactionSync"); 39 | test.ok("startTransaction" in conn, "startTransaction"); 40 | test.done(); 41 | }; 42 | 43 | exports.SafeConnectionBinding = function (test) { 44 | test.expect(22); 45 | test.ok("createConnection" in safe_con, "createConnection"); 46 | var conn = safe_con.createConnection(); 47 | test.ok(conn, "Connection created"); 48 | test.ok("connectSync" in conn, "connectSync"); 49 | test.ok("connect" in conn, "connect"); 50 | test.ok("connected" in conn, "connected"); 51 | test.ok("disconnect" in conn, "disconnect"); 52 | test.ok("querySync" in conn, "querySync"); 53 | test.ok("query" in conn, "query"); 54 | test.ok("addFBevent" in conn,"addFBevent"); 55 | test.ok("deleteFBevent" in conn,"deleteFBevent"); 56 | test.ok("commit" in conn, "commit"); 57 | test.ok("commitSync" in conn, "commitSync"); 58 | test.ok("inTransaction" in conn, "inTransaction"); 59 | test.ok("rollback" in conn, "rollback"); 60 | test.ok("rollbackSync" in conn, "rollbackSync"); 61 | test.ok("inAsyncCall" in conn, "inAsyncCall"); 62 | test.ok("prepareSync" in conn, "prepareSync"); 63 | test.ok("newBlobSync" in conn, "newBlobSync"); 64 | test.ok("startTransactionSync" in conn, "startTransactionSync"); 65 | test.ok("startTransaction" in conn, "startTransaction"); 66 | test.ok("startNewTransactionSync" in conn, "startNewTransactionSync"); 67 | test.ok("startNewTransaction" in conn, "startNewTransaction"); 68 | test.done(); 69 | }; 70 | 71 | exports.FBResultBinding = function(test){ 72 | test.expect(4); 73 | var conn = new fb_binding.Connection; 74 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 75 | test.ok(conn.connected,"Connected to database"); 76 | var res = conn.querySync("select * from rdb$database"); 77 | test.ok("fetchSync" in res, "fetchSync"); 78 | test.ok("fetch" in res, "fetch"); 79 | test.ok("inAsyncCall" in res, "inAsyncCall"); 80 | conn.disconnect(); 81 | test.done(); 82 | } 83 | 84 | exports.FBblobBinding = function(test){ 85 | test.expect(6); 86 | var blob = fb_binding.FBblob.prototype; 87 | test.ok("_readSync" in blob, 'readSync'); 88 | test.ok("_read" in blob, 'read'); 89 | test.ok("_openSync" in blob, '_openSync'); 90 | test.ok("_closeSync" in blob, '_closeSync'); 91 | test.ok("_writeSync" in blob, '_writeSync'); 92 | test.ok("_write" in blob, '_write'); 93 | test.done(); 94 | } 95 | 96 | exports.FBstatementBinding = function(test){ 97 | test.expect(4); 98 | var stmt = fb_binding.FBStatement.prototype; 99 | test.ok("execSync" in stmt, 'execSync'); 100 | test.ok("exec" in stmt, 'exec'); 101 | test.ok("execInTransSync" in stmt, 'execInTransSync'); 102 | test.ok("execInTrans" in stmt, 'execInTrans'); 103 | // test.ok("inAsyncCall" in stmt, "inAsyncCall"); 104 | test.done(); 105 | } 106 | 107 | exports.TransactionBinding = function (test) { 108 | test.expect(11); 109 | var trans = fb_binding.Transaction.prototype; 110 | test.ok("start" in trans, "commit"); 111 | test.ok("startSync" in trans, "startSync"); 112 | test.ok("commit" in trans, "commit"); 113 | test.ok("commitSync" in trans, "commitSync"); 114 | test.ok("rollback" in trans, "rollback"); 115 | test.ok("rollbackSync" in trans, "rollbackSync"); 116 | test.ok("querySync" in trans, "querySync"); 117 | test.ok("query" in trans, "query"); 118 | test.ok("prepareSync" in trans, "prepareSync"); 119 | var conn = new fb_binding.Connection; 120 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 121 | var transInsts = conn.startNewTransactionSync(); 122 | test.ok("inTransaction" in transInsts, "inTransaction"); 123 | test.ok("inAsyncCall" in transInsts, "inAsyncCall"); 124 | test.done(); 125 | } 126 | 127 | exports.Lc_ctype = function(test) { 128 | test.expect(3); 129 | var conn = safe_con.createConnection({ 130 | lc_ctype : "ASCII", 131 | lc_ctype_decode: function (buffer) { 132 | // test buffer 133 | test.ok(buffer instanceof Buffer, "buffer expected"); 134 | return buffer; 135 | } 136 | }); 137 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 138 | var res = conn.querySync("select first 1 RDB$RELATION_NAME from rdb$relations").fetchSync("all",true); 139 | test.ok(res,"Query"); 140 | test.ok(res[0]["RDB$RELATION_NAME"] instanceof Buffer,"Buffer returned"); 141 | test.done(); 142 | } 143 | 144 | -------------------------------------------------------------------------------- /firebird.js: -------------------------------------------------------------------------------- 1 | var binding = require("./build/Release/binding"); 2 | var stream = require("stream"); 3 | var util = require("util"); 4 | var events = require('events'); 5 | 6 | var SchunkSize = 4*1024; 7 | 8 | 9 | var Connection = binding.Connection; 10 | var FBEventEmitter = binding.FBEventEmitter; 11 | var FBResult = binding.FBResult; 12 | var FBStatement = binding.FBStatement; 13 | var FBblob = binding.FBblob; 14 | var Transaction = binding.Transaction; 15 | 16 | // inherit FBEventEmitter (and all descendants ) from events.EventEmitter 17 | FBEventEmitter.prototype.__proto__ = events.EventEmitter.prototype; 18 | 19 | 20 | function MakeSafe(obj,meth){ 21 | var superm = obj.prototype[meth]; 22 | obj.prototype[meth] = function safe(){ 23 | if(this.inAsyncCall){ 24 | var self = this; 25 | var args = arguments; 26 | this.once("fbStopAsync",function(){ 27 | safe.apply(self,args); 28 | //superm.apply(self,args); 29 | }); 30 | } 31 | else { 32 | superm.apply(this,arguments); 33 | } 34 | } 35 | } 36 | 37 | MakeSafe(Connection,"query"); 38 | MakeSafe(Connection,"commit"); 39 | MakeSafe(Connection, "rollback"); 40 | MakeSafe(Connection, "startTransaction"); 41 | //MakeSafe(Connection, "startNewTransaction"); 42 | MakeSafe(FBResult,"fetch"); 43 | MakeSafe(FBStatement, "exec"); 44 | MakeSafe(FBStatement, "execInTrans"); 45 | MakeSafe(FBblob,"_read"); 46 | MakeSafe(FBblob, "_write"); 47 | MakeSafe(Transaction, "start"); 48 | MakeSafe(Transaction, "query"); 49 | MakeSafe(Transaction, "commit"); 50 | MakeSafe(Transaction, "rollback"); 51 | 52 | var superConnect = Connection.prototype.connect; 53 | Connection.prototype.connect = function(db,user,pass,role,cb){ 54 | var obj = this; 55 | superConnect.call(this,db,user,pass,role,function (err){ 56 | if(err && (!cb || obj.listeners('error').length)) obj.emit('error',err); 57 | else obj.emit('connected'); 58 | if(cb) cb(err); 59 | }); 60 | }; 61 | 62 | 63 | var superQuery = Connection.prototype.query; 64 | Connection.prototype.query = function(sql,cb){ 65 | var obj = this; 66 | superQuery.call(this,sql,function(err,res){ 67 | if(err && (!cb || obj.listeners('error').length)) obj.emit('error',err); 68 | else obj.emit('result',res); 69 | if(cb) cb(err,res); 70 | }); 71 | }; 72 | 73 | var superStartNewTransaction = Connection.prototype.startNewTransaction; 74 | Connection.prototype.startNewTransaction = function (cb) { 75 | var obj = this; 76 | var tr = superStartNewTransaction.call(this, function (err) { 77 | if (err && (!cb || obj.listeners('error').length)) obj.emit('error', err); 78 | if (cb) cb(err, tr); 79 | }); 80 | } 81 | 82 | 83 | binding.FBblob.prototype._readAll = function(initialSize, chunkSize, callback){ 84 | if(initialSize instanceof Function) 85 | { 86 | callback = initialSize; 87 | chunkSize = null; 88 | initialSize = null; 89 | } 90 | else 91 | if(chunkSize instanceof Function) 92 | { 93 | callback = chunkSize; 94 | chunkSize = null; 95 | } 96 | 97 | if(!chunkSize) chunkSize = 1024; 98 | if(!initialSize) initialSize = 0; 99 | 100 | var chunk = new Buffer(chunkSize); 101 | var res = new Buffer(initialSize); 102 | var cPos = 0; 103 | 104 | 105 | this._openSync(); 106 | var self = this; 107 | this._read(chunk, function receiver(err, b, isLast) { 108 | if(err) 109 | { 110 | self.emit('error',err); 111 | } 112 | else 113 | if(!isLast) 114 | { 115 | self.emit('data', b); 116 | if(res.length<=(cPos+b.length)) 117 | { 118 | var nr = new Buffer(cPos + b.length); 119 | res.copy(nr); 120 | res = nr; 121 | } 122 | b.copy(res,cPos); 123 | cPos = cPos + b.length; 124 | self._read(chunk,receiver); 125 | } 126 | else 127 | { 128 | self._closeSync(); 129 | self.emit('end',res,cPos); 130 | if(callback) callback(null, res, cPos); 131 | } 132 | }); 133 | 134 | 135 | } 136 | 137 | 138 | exports.createConnection = function (options) { 139 | var c = new Connection(options); 140 | return c; 141 | }; 142 | 143 | 144 | // Allows further extension 145 | exports.binding = binding; 146 | 147 | function Stream(blob){ 148 | 149 | 150 | if(!(blob instanceof binding.FBblob )) { 151 | throw new Error('Expecting blob'); 152 | //blob = new binding.FBblob(); 153 | } 154 | stream.Stream.call(this); 155 | this._blob = blob; 156 | this._pendingWrites = 0; 157 | this.readable = false; 158 | this.writeable = false; 159 | 160 | if(blob.isReadable) 161 | { 162 | this._blob._openSync(); 163 | this.readable = true; 164 | this._paused = true; 165 | } 166 | else 167 | this.writable = true; 168 | }; 169 | 170 | util.inherits(Stream, stream.Stream); 171 | exports.Stream = Stream; 172 | 173 | function ReadStream(strm) { 174 | 175 | if (strm._buf == null) { 176 | strm._buf = new Buffer(SchunkSize); 177 | } 178 | 179 | strm._blob._read(strm._buf, function s_rcv(err, b, isLast) { 180 | 181 | if (err) { 182 | return strm.emit('error', err); 183 | } 184 | strm.emit('data', b); 185 | 186 | if (!strm._paused && !isLast) strm._blob._read(strm._buf, s_rcv); 187 | 188 | if (isLast) strm.emit('end'); 189 | }); 190 | }; 191 | 192 | Stream.prototype.pause = function(){ 193 | this._paused = true; 194 | }; 195 | 196 | Stream.prototype.resume = function(){ 197 | this._paused = false; 198 | ReadStream(this); 199 | }; 200 | 201 | Stream.prototype.destroy = function () { 202 | this._destroyed = true; 203 | this.check_destroyed(); 204 | return this; 205 | }; 206 | 207 | Stream.prototype.check_destroyed = function () { 208 | if (this._destroyed && !this._pendingWrites) { 209 | this._blob._closeSync(); 210 | this.emit('close'); 211 | } 212 | } 213 | 214 | Stream.prototype.write = function (data, encoding, fd) { 215 | if (this._destroyed) { 216 | throw new Error("Stream closed"); 217 | } 218 | var self = this; 219 | this._pendingWrites++; 220 | this._blob._write(data, function (err) { 221 | self._pendingWrites--; 222 | if (err) self.emit('error', err); 223 | self.check_destroyed(); 224 | }); 225 | } 226 | 227 | Stream.prototype.end = function(data, encoding, fd) { 228 | var self = this; 229 | if (data) { 230 | this._pendingWrites++; 231 | this._blob._write(data, function (err) { 232 | self._pendingWrites--; 233 | if (err) self.emit('error', err); 234 | self.destroy(); 235 | }) 236 | } 237 | else this.destroy(); 238 | return this; 239 | } 240 | 241 | -------------------------------------------------------------------------------- /src/fb-bindings-transaction.cc: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "./fb-bindings-transaction.h" 4 | #include "./fb-bindings-connection.h" 5 | 6 | Nan::Persistent Transaction::constructor_template; 7 | 8 | void Transaction::Initialize(v8::Local target) { 9 | Local t = Nan::New(Transaction::New); 10 | constructor_template.Reset(t); 11 | 12 | t->Inherit(Nan::New(FBEventEmitter::constructor_template)); 13 | t->SetClassName(Nan::New("Transaction").ToLocalChecked()); 14 | 15 | Local instance_template = t->InstanceTemplate(); 16 | 17 | Nan::SetPrototypeMethod(t, "commit", Commit); 18 | Nan::SetPrototypeMethod(t, "rollback", Rollback); 19 | Nan::SetPrototypeMethod(t, "start", Start); 20 | 21 | Nan::SetPrototypeMethod(t, "commitSync", CommitSync); 22 | Nan::SetPrototypeMethod(t, "rollbackSync", RollbackSync); 23 | Nan::SetPrototypeMethod(t, "startSync", StartSync); 24 | 25 | Nan::SetPrototypeMethod(t, "querySync", QuerySync); 26 | Nan::SetPrototypeMethod(t, "query", Query); 27 | 28 | Nan::SetPrototypeMethod(t, "prepareSync", PrepareSync); 29 | 30 | instance_template->SetInternalFieldCount(1); 31 | 32 | Local instance_t = t->InstanceTemplate(); 33 | Nan::SetAccessor(instance_t, Nan::New("inAsyncCall").ToLocalChecked(), InAsyncGetter); 34 | Nan::SetAccessor(instance_t, Nan::New("inTransaction").ToLocalChecked(), InTransactionGetter); 35 | 36 | Nan::Set(target, Nan::New("Transaction").ToLocalChecked(), Nan::GetFunction(t).ToLocalChecked()); 37 | } 38 | 39 | NAN_METHOD(Transaction::New) { 40 | Nan::HandleScope scope; 41 | 42 | REQ_EXT_ARG(0, js_connection); 43 | 44 | Connection *conn; 45 | conn = static_cast(js_connection->Value()); 46 | 47 | Transaction *res = new Transaction(conn); 48 | res->Wrap(info.This()); 49 | 50 | info.GetReturnValue().Set(info.This()); 51 | } 52 | 53 | 54 | bool Transaction::commit_transaction() 55 | { 56 | if (isc_commit_transaction(status, &trans)) 57 | { 58 | trans = 0; 59 | return false; 60 | } 61 | trans = 0; 62 | return true; 63 | } 64 | 65 | bool Transaction::rollback_transaction() 66 | { 67 | if (isc_rollback_transaction(status, &trans)) 68 | { 69 | trans = 0; 70 | return false; 71 | } 72 | trans = 0; 73 | return true; 74 | } 75 | 76 | bool Transaction::start_transaction() 77 | { 78 | if (!trans) 79 | { 80 | if (isc_start_transaction(status, &trans, 1, &connection->db, 0, NULL)) 81 | { 82 | trans = 0; 83 | return false; 84 | } 85 | return true; 86 | } 87 | 88 | strncpy(err_message, "Old transaction should be commited or rolled back.", MAX_ERR_MSG_LEN); 89 | return false; 90 | } 91 | 92 | void Transaction::EIO_After_TransactionRequest(uv_work_t *req) 93 | { 94 | // uv_unref(uv_default_loop()); 95 | Nan::HandleScope scope; 96 | struct transaction_request *tr_req = (struct transaction_request *)(req->data); 97 | delete req; 98 | Local argv[1]; 99 | 100 | if (!tr_req->result) { 101 | argv[0] = Nan::Error(*Nan::Utf8String(ERR_MSG(tr_req->transaction, Connection))); 102 | } 103 | else { 104 | argv[0] = Nan::Null(); 105 | } 106 | 107 | Nan::TryCatch try_catch; 108 | Nan::Call(*tr_req->callback, 1, argv); 109 | if (try_catch.HasCaught()) { 110 | Nan::FatalException(try_catch); 111 | } 112 | if(!tr_req->transaction->persistent().IsEmpty()) { 113 | tr_req->transaction->stop_async(); 114 | tr_req->transaction->Unref(); 115 | } 116 | free(tr_req); 117 | 118 | 119 | } 120 | 121 | void Transaction::EIO_TransactionRequest(uv_work_t *req) 122 | { 123 | struct transaction_request *tr_req = (struct transaction_request *)(req->data); 124 | switch (tr_req->type) { 125 | case rCommit: 126 | tr_req->result = tr_req->transaction->commit_transaction(); 127 | break; 128 | case rRollback: 129 | tr_req->result = tr_req->transaction->rollback_transaction(); 130 | break; 131 | case rStart: 132 | tr_req->result = tr_req->transaction->start_transaction(); 133 | } 134 | return; 135 | } 136 | 137 | void Transaction::makeTrRequest(const Nan::FunctionCallbackInfo& info, TransReqType type) { 138 | struct transaction_request *tr_req = 139 | (struct transaction_request *)calloc(1, sizeof(struct transaction_request)); 140 | if (!tr_req) { 141 | Nan::LowMemoryNotification(); 142 | return Nan::ThrowError("Could not allocate memory."); 143 | 144 | } 145 | 146 | if (info.Length() < 1) { 147 | return Nan::ThrowError("Expecting Callback Function argument"); 148 | } 149 | 150 | tr_req->transaction = this; 151 | tr_req->callback = new Nan::Callback(Local::Cast(info[0])); 152 | tr_req->type = type; 153 | 154 | 155 | 156 | tr_req->transaction->start_async(); 157 | 158 | uv_work_t* req = new uv_work_t(); 159 | req->data = tr_req; 160 | uv_queue_work(uv_default_loop(), req, EIO_TransactionRequest, (uv_after_work_cb)EIO_After_TransactionRequest); 161 | 162 | if(!tr_req->transaction->persistent().IsEmpty()) { 163 | tr_req->transaction->Ref(); 164 | } 165 | 166 | 167 | } 168 | 169 | void Transaction::InstCommit(const Nan::FunctionCallbackInfo& info) 170 | { 171 | if (!trans) { 172 | return Nan::ThrowError("Transaction not started"); 173 | } 174 | makeTrRequest(info, rCommit); 175 | } 176 | 177 | NAN_METHOD(Transaction::Commit) 178 | { 179 | Nan::ObjectWrap::Unwrap(info.This())->InstCommit(info); 180 | } 181 | 182 | void Transaction::InstRollback(const Nan::FunctionCallbackInfo& info) 183 | { 184 | if (!trans) { 185 | return Nan::ThrowError("Transaction not started"); 186 | } 187 | makeTrRequest(info, rRollback); 188 | } 189 | 190 | NAN_METHOD(Transaction::Rollback) 191 | { 192 | Nan::ObjectWrap::Unwrap(info.This())->InstRollback(info); 193 | } 194 | 195 | void Transaction::InstStart(const Nan::FunctionCallbackInfo& info) 196 | { 197 | if (trans) { 198 | return Nan::ThrowError("Transaction already started"); 199 | } 200 | makeTrRequest(info, rStart); 201 | } 202 | 203 | NAN_METHOD(Transaction::Start) 204 | { 205 | Nan::ObjectWrap::Unwrap(info.This())->InstStart(info); 206 | } 207 | 208 | Transaction::Transaction(Connection *conn) { 209 | connection = conn; 210 | trans = NULL; 211 | } 212 | Transaction::~Transaction() { 213 | if (trans) { 214 | commit_transaction(); 215 | } 216 | } 217 | 218 | NAN_GETTER(Transaction::InTransactionGetter) 219 | { 220 | Nan::HandleScope scope; 221 | Transaction *transaction = Nan::ObjectWrap::Unwrap(info.This()); 222 | info.GetReturnValue().Set(Nan::New(transaction->trans)); 223 | } 224 | 225 | 226 | NAN_METHOD(Transaction::CommitSync) 227 | { 228 | Nan::HandleScope scope; 229 | Transaction *transaction = Nan::ObjectWrap::Unwrap(info.This()); 230 | if (!transaction->trans) { 231 | return Nan::ThrowError("Transaction not started"); 232 | } 233 | bool r = transaction->commit_transaction(); 234 | if (!r) { 235 | return Nan::ThrowError( 236 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("While commitSync - ").ToLocalChecked(), ERR_MSG(transaction, Transaction))); 237 | } 238 | } 239 | 240 | NAN_METHOD(Transaction::RollbackSync) 241 | { 242 | Nan::HandleScope scope; 243 | Transaction *transaction = Nan::ObjectWrap::Unwrap(info.This()); 244 | if (!transaction->trans) { 245 | return Nan::ThrowError("Transaction not started"); 246 | } 247 | 248 | bool r = transaction->rollback_transaction(); 249 | if (!r) { 250 | return Nan::ThrowError( 251 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("While rollbackSync - ").ToLocalChecked(), ERR_MSG(transaction, Transaction))); 252 | } 253 | } 254 | 255 | NAN_METHOD(Transaction::StartSync) 256 | { 257 | Nan::HandleScope scope; 258 | Transaction *transaction = Nan::ObjectWrap::Unwrap(info.This()); 259 | if (transaction->trans) { 260 | return Nan::ThrowError("Transaction already started"); 261 | } 262 | bool r = transaction->start_transaction(); 263 | if (!r) { 264 | return Nan::ThrowError( 265 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("While startSync - ").ToLocalChecked(), ERR_MSG(transaction, Transaction))); 266 | } 267 | 268 | return; 269 | } 270 | 271 | 272 | NAN_METHOD(Transaction::QuerySync) 273 | { 274 | Nan::HandleScope scope; 275 | Transaction *transaction = Nan::ObjectWrap::Unwrap(info.This()); 276 | transaction->connection->InstQuerySync(info, transaction); 277 | } 278 | 279 | NAN_METHOD(Transaction::Query) { 280 | Nan::HandleScope scope; 281 | Transaction *transaction = Nan::ObjectWrap::Unwrap(info.This()); 282 | transaction->connection->InstQuery(info, transaction); 283 | 284 | } 285 | 286 | NAN_METHOD(Transaction::PrepareSync) { 287 | Nan::HandleScope scope; 288 | Transaction *transaction = Nan::ObjectWrap::Unwrap(info.This()); 289 | transaction->connection->InstPrepareSync(info, transaction); 290 | } -------------------------------------------------------------------------------- /src/fb-bindings-statement.cc: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright by Denys Khanzhiyev aka xdenser 3 | * 4 | * See license text in LICENSE file 5 | */ 6 | #define BUILDING_NODE_EXTENSION 1 7 | #include "./fb-bindings-statement.h" 8 | 9 | 10 | Nan::Persistent FBStatement::constructor_template; 11 | 12 | void 13 | FBStatement::Initialize (v8::Local target) 14 | { 15 | Nan::HandleScope scope; 16 | 17 | Local t = Nan::New(FBStatement::New); 18 | constructor_template.Reset(t); 19 | 20 | t->Inherit(Nan::New(FBResult::constructor_template)); 21 | t->SetClassName(Nan::New("FBStatement").ToLocalChecked()); 22 | 23 | Local instance_template = 24 | t->InstanceTemplate(); 25 | 26 | Nan::SetPrototypeMethod(t, "execSync", ExecSync); 27 | Nan::SetPrototypeMethod(t, "execInTransSync", ExecInTransSync); 28 | Nan::SetPrototypeMethod(t, "exec", Exec); 29 | Nan::SetPrototypeMethod(t, "execInTrans", ExecInTrans); 30 | 31 | 32 | instance_template->SetInternalFieldCount(1); 33 | 34 | Nan::Set(target, Nan::New("FBStatement").ToLocalChecked(), Nan::GetFunction(t).ToLocalChecked()); 35 | 36 | } 37 | 38 | NAN_METHOD(FBStatement::New) 39 | { 40 | Nan::HandleScope scope; 41 | 42 | REQ_EXT_ARG(0, js_in_sqldap); 43 | REQ_EXT_ARG(1, js_out_sqldap); 44 | REQ_EXT_ARG(2, js_stmtp); 45 | REQ_EXT_ARG(3, js_connection); 46 | 47 | XSQLDA *insqldap; 48 | insqldap = static_cast(js_in_sqldap->Value()); 49 | XSQLDA *outsqldap; 50 | outsqldap = static_cast(js_out_sqldap->Value()); 51 | isc_stmt_handle *astmtp; 52 | astmtp = static_cast(js_stmtp->Value()); 53 | Connection *conn; 54 | conn = static_cast(js_connection->Value()); 55 | 56 | FBStatement *res = new FBStatement(insqldap,outsqldap, astmtp, conn); 57 | res->Wrap(info.This()); 58 | 59 | info.GetReturnValue().Set(info.This()); 60 | } 61 | 62 | void FBStatement::InstExecSync(const Nan::FunctionCallbackInfo& info, Transaction* transaction, int firstArg) 63 | { 64 | Nan::HandleScope scope; 65 | 66 | FBResult::set_params(in_sqlda, info, firstArg); 67 | 68 | if(retres) 69 | { 70 | isc_dsql_free_statement(status, &stmt, DSQL_close); 71 | if (status[1]) 72 | { 73 | return Nan::ThrowError( 74 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("In FBStatement::execSync, free_statement - ").ToLocalChecked(),ERR_MSG(this, FBStatement))); 75 | } 76 | } 77 | 78 | Transaction *tr = transaction; 79 | 80 | if (!connection->check_trans(&tr)) { 81 | return Nan::ThrowError("Failed to get default transaction"); 82 | } 83 | 84 | 85 | if (isc_dsql_execute2(status, &tr->trans, &stmt, SQL_DIALECT_V6, in_sqlda, statement_type == isc_info_sql_stmt_select ? NULL:sqldap)) 86 | { 87 | return Nan::ThrowError( 88 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("In FBStatement::execSync - ").ToLocalChecked(),ERR_MSG(this, FBStatement))); 89 | } 90 | 91 | if(sqldap->sqld && statement_type != isc_info_sql_stmt_select){ 92 | Local js_result_row; 93 | js_result_row = getCurrentRow(true); 94 | info.GetReturnValue().Set(js_result_row); 95 | } 96 | 97 | 98 | if(!sqldap->sqld) 99 | return; 100 | 101 | retres = true; 102 | 103 | return; 104 | 105 | } 106 | 107 | NAN_METHOD(FBStatement::ExecSync) 108 | { 109 | Nan::HandleScope scope; 110 | 111 | FBStatement *fb_stmt = Nan::ObjectWrap::Unwrap(info.This()); 112 | fb_stmt->InstExecSync(info, NULL, 0); 113 | } 114 | 115 | NAN_METHOD(FBStatement::ExecInTransSync) 116 | { 117 | Nan::HandleScope scope; 118 | 119 | if (info.Length() < 1) { 120 | Nan::ThrowError("At least 1 argument expected"); 121 | return; 122 | } 123 | 124 | Local handle = Nan::To(info[0]).ToLocalChecked(); 125 | 126 | if (!Nan::New(Transaction::constructor_template)->HasInstance(handle)) { 127 | Nan::ThrowError("Tansaction expected as first argument"); 128 | return; 129 | } 130 | 131 | FBStatement *fb_stmt = Nan::ObjectWrap::Unwrap(info.This()); 132 | Transaction *tr = Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); 133 | fb_stmt->InstExecSync(info, tr, 1); 134 | } 135 | 136 | void FBStatement::EIO_After_Exec(uv_work_t *req) 137 | { 138 | Nan::HandleScope scope; 139 | 140 | struct exec_request *e_req = (struct exec_request *)(req->data); 141 | delete req; 142 | FBStatement *fb_stmt = e_req->statement; 143 | Local argv[3]; 144 | Local event; 145 | int argc = 0; 146 | if(!e_req->result) 147 | { 148 | argv[0] = Nan::Error(*Nan::Utf8String( 149 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("In FBStatement::EIO_After_Exec - ").ToLocalChecked(),ERR_MSG(fb_stmt, FBStatement)))); 150 | argc = 0; 151 | event = Nan::New("error").ToLocalChecked(); 152 | } 153 | else 154 | { 155 | argv[0] = Nan::Null(); 156 | argc = 1; 157 | event = Nan::New("result").ToLocalChecked(); 158 | if (fb_stmt->sqldap->sqld) { 159 | fb_stmt->retres = true; 160 | if (fb_stmt->statement_type != isc_info_sql_stmt_select) 161 | { 162 | argv[1] = fb_stmt->getCurrentRow(true); 163 | argc = 2; 164 | } 165 | } 166 | } 167 | ((FBEventEmitter*) fb_stmt)->Emit(event,argc,argv); 168 | fb_stmt->stop_async(); 169 | fb_stmt->Unref(); 170 | free(e_req); 171 | } 172 | 173 | void FBStatement::EIO_Exec(uv_work_t *req) 174 | { 175 | struct exec_request *e_req = (struct exec_request *)(req->data); 176 | FBStatement *fb_stmt = e_req->statement; 177 | 178 | Transaction *tr = e_req->trans; 179 | 180 | if (!fb_stmt->connection->check_trans(&tr)) { 181 | return Nan::ThrowError("Failed to get default transaction"); 182 | } 183 | 184 | 185 | if (isc_dsql_execute2(fb_stmt->status, &tr->trans, &fb_stmt->stmt, SQL_DIALECT_V6, fb_stmt->in_sqlda, fb_stmt->statement_type == isc_info_sql_stmt_select ? NULL:fb_stmt->sqldap)) 186 | e_req->result = false; 187 | else 188 | e_req->result = true; 189 | 190 | 191 | return ; 192 | } 193 | 194 | void FBStatement::InstExec(const Nan::FunctionCallbackInfo& info, Transaction* transaction, int firstArg) 195 | { 196 | Nan::HandleScope scope; 197 | 198 | struct exec_request *e_req = 199 | (struct exec_request *)calloc(1, sizeof(struct exec_request)); 200 | 201 | if (!e_req) { 202 | Nan::LowMemoryNotification(); 203 | return Nan::ThrowError("Could not allocate memory."); 204 | } 205 | 206 | FBResult::set_params(in_sqlda, info, firstArg); 207 | if(retres) 208 | { 209 | isc_dsql_free_statement(status, &stmt, DSQL_close); 210 | if (status[1]) 211 | { 212 | return Nan::ThrowError( 213 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("In FBStatement::exec, free_statement - ").ToLocalChecked(),ERR_MSG(this, FBStatement))); 214 | } 215 | } 216 | 217 | e_req->statement = this; 218 | e_req->trans = transaction; 219 | 220 | start_async(); 221 | 222 | uv_work_t* req = new uv_work_t(); 223 | req->data = e_req; 224 | uv_queue_work(uv_default_loop(), req, EIO_Exec, (uv_after_work_cb)EIO_After_Exec); 225 | Ref(); 226 | } 227 | 228 | NAN_METHOD(FBStatement::Exec) 229 | { 230 | Nan::HandleScope scope; 231 | FBStatement *fb_stmt = Nan::ObjectWrap::Unwrap(info.This()); 232 | fb_stmt->InstExec(info, NULL, 0); 233 | } 234 | 235 | NAN_METHOD(FBStatement::ExecInTrans) 236 | { 237 | Nan::HandleScope scope; 238 | 239 | if (info.Length() < 1) { 240 | Nan::ThrowError("At least 1 argument expected"); 241 | return; 242 | } 243 | 244 | Local handle = Nan::To(info[0]).ToLocalChecked(); 245 | if (!Nan::New(Transaction::constructor_template)->HasInstance(handle)) { 246 | Nan::ThrowError("Tansaction expected as first argument"); 247 | return; 248 | } 249 | 250 | FBStatement *fb_stmt = Nan::ObjectWrap::Unwrap(info.This()); 251 | Transaction *tr = Nan::ObjectWrap::Unwrap(handle); 252 | fb_stmt->InstExec(info, tr, 1); 253 | } 254 | 255 | 256 | 257 | FBStatement::FBStatement(XSQLDA *insqlda, XSQLDA *outsqlda, isc_stmt_handle *astmtp, Connection* aconn) : 258 | FBResult (outsqlda,astmtp,aconn) 259 | { 260 | // conn = aconn; 261 | in_sqlda = insqlda; 262 | // out_sqlda = outsqlda; 263 | // stmt = *astmtp; 264 | retres = false; 265 | strncpy(cursor, "test_cursor", sizeof(cursor)); 266 | // Get sql info 267 | short l; 268 | static char stmt_info[] = { isc_info_sql_stmt_type }; 269 | char info_buffer[20]; 270 | 271 | if (!isc_dsql_sql_info(status, astmtp, sizeof (stmt_info), stmt_info, 272 | sizeof (info_buffer), info_buffer)) 273 | { 274 | l = (short) isc_vax_integer((char *) info_buffer + 1, 2); 275 | statement_type = isc_vax_integer((char *) info_buffer + 3, l); 276 | } 277 | } 278 | 279 | FBStatement::~FBStatement() 280 | { 281 | if(in_sqlda) { 282 | FBResult::clean_sqlda(in_sqlda); 283 | free(in_sqlda); 284 | } 285 | } 286 | 287 | 288 | -------------------------------------------------------------------------------- /tests/def/test-datatypes.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright by Denys Khanzhiyev 3 | See license text in LICENSE file 4 | */ 5 | 6 | // Load configuration 7 | var cfg = require("../config").cfg; 8 | var sys = require("util"); 9 | 10 | // Require modules 11 | var 12 | fb_binding = require("../../firebird"); 13 | var 14 | testCase = require("nodeunit").testCase; 15 | 16 | var util = { 17 | getDataTypeResult: function(datatype,value){ 18 | this.conn.querySync('create table DT_TEST_TABLE ( test_field '+datatype+')'); 19 | this.conn.querySync('insert into DT_TEST_TABLE (test_field) values ('+value+')'); 20 | this.conn.commitSync(); 21 | return this.conn.querySync('select first 1 test_field from DT_TEST_TABLE').fetchSync("all",false)[0][0]; 22 | }, 23 | quote:function(val){ return "'"+val+"'";}, 24 | JSDateToSQLDT: function(date){ 25 | return date.getFullYear()+'-'+(date.getMonth()+1)+'-'+date.getDate()+' '+date.getHours()+':'+date.getMinutes()+':'+date.getSeconds()+'.'+date.getMilliseconds(); 26 | }, 27 | JSDateToSQLD: function(date){ 28 | return date.getFullYear()+'-'+(date.getMonth()+1)+'-'+date.getDate(); 29 | }, 30 | JSTimeToSQLT:function(time){ 31 | return time.getHours()+':'+time.getMinutes()+':'+time.getSeconds()+'.'+time.getMilliseconds(); 32 | } 33 | }; 34 | 35 | 36 | module.exports = testCase({ 37 | setUp: function(callback){ 38 | this.conn = new fb_binding.createConnection(); 39 | this.conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 40 | callback(); 41 | }, 42 | tearDown: function(callback){ 43 | this.conn.disconnect(); 44 | this.conn = null; 45 | var conn = new fb_binding.createConnection(); 46 | conn.connectSync(cfg.db, cfg.user, cfg.password, cfg.role); 47 | conn.querySync('drop table DT_TEST_TABLE'); 48 | conn.disconnect(); 49 | callback(); 50 | }, 51 | char1:function(test){ 52 | test.expect(1); 53 | var Data = "Z"; 54 | var res = util.getDataTypeResult.call(this,'char(1)',util.quote(Data)); 55 | test.equal(res,Data,'char(1)'); 56 | test.done(); 57 | }, 58 | char10:function(test){ 59 | test.expect(1); 60 | var Data = "0123456789"; 61 | var res = util.getDataTypeResult.call(this,'char(10)',util.quote(Data)); 62 | test.equal(res,Data,'char(10)'); 63 | test.done(); 64 | }, 65 | charMAX:function(test){ 66 | // return; 67 | test.expect(1); 68 | var len = Math.ceil(32737/4); 69 | var Data = ''; 70 | var chars = 'abcdefghABSD01234689q'; 71 | for(var i=0; i 0) { 88 | regevents.push({ name: event, count: count }); 89 | } 90 | }); 91 | conn.addFBevent(eName); 92 | // console.log('after add Event1'); 93 | // conn.addFBevent('another'); 94 | // console.log('after add another'); 95 | // Wait 1 sec for event 96 | setTimeout(function () { 97 | conn.disconnect(); 98 | CleanUp(); 99 | test.equal(regevents.length, 0, "incorrect number of callback calls " + JSON.stringify(regevents)); 100 | test.done(); 101 | }, 1000); 102 | 103 | } 104 | 105 | exports.oneEvent = function(test) { 106 | test.expect(4); 107 | Init(); 108 | 109 | var conn = Connect(); 110 | test.ok(conn.connected,"Not connected to database"); 111 | var eName = "Event1"; 112 | var finished = false; 113 | conn.addFBevent(eName); 114 | var regevents = []; 115 | conn.on("fbevent", function (event, count) { 116 | if (count > 0) { 117 | regevents.push({ name: event, count: count }); 118 | finished = true; 119 | } 120 | }); 121 | 122 | GenEvent(eName); 123 | 124 | // Wait 5 sec for event 125 | WaitForFinish(function(){ return finished; }, 126 | function(){ 127 | conn.disconnect(); 128 | CleanUp(); 129 | test.equal(regevents.length, 1, "incorrect number of callback calls" + JSON.stringify(regevents)); 130 | test.equal(regevents[0].name, eName, "incorrect event name"); 131 | test.equal(regevents[0].count, 1, "wrong number of events"); 132 | test.done(); 133 | }, 5000); 134 | } 135 | 136 | exports.oneEventBetween = function(test) { 137 | test.expect(4); 138 | Init(); 139 | 140 | var conn = Connect(); 141 | test.ok(conn.connected,"Connected to database"); 142 | var eName = "Event1"; 143 | var finished = false; 144 | var regevents = []; 145 | conn.on("fbevent", function (event, count) { 146 | console.log("event", event, count); 147 | if (count > 0) { 148 | regevents.push({ name: event, count: count }); 149 | finished = true; 150 | } 151 | }); 152 | 153 | conn.addFBevent(eName); 154 | 155 | GenEvent(eName); 156 | 157 | conn.addFBevent("strange"); 158 | 159 | // Wait 5 sec for event 160 | WaitForFinish(function(){ return finished; }, 161 | function(){ 162 | conn.disconnect(); 163 | CleanUp(); 164 | test.equal(regevents.length, 1, "incorrect number of callback calls" + JSON.stringify(regevents)); 165 | test.equal(regevents[0].name, eName, "incorrect event name"); 166 | test.equal(regevents[0].count, 1, "wrong number of events"); 167 | test.done(); 168 | }, 5000); 169 | } 170 | 171 | 172 | exports.TensOfEvents = function(test){ 173 | test.expect(2); 174 | Init(); 175 | var conn = Connect(); 176 | test.ok(conn.connected,"Connected to database"); 177 | 178 | var evcount = 0; 179 | var expected_count = 100; //400;? 180 | conn.on("fbevent", function(event,count){ 181 | //console.log("->"+event+" "+count); 182 | evcount++; 183 | }); 184 | 185 | var events = []; 186 | var en; 187 | for(var i = 0; i"+event+" "+count); 219 | called = true; 220 | }); 221 | var eN = 'eventName'; 222 | conn.addFBevent(eN); 223 | conn.deleteFBevent(eN); 224 | GenEvent(eN); 225 | 226 | setTimeout(function(){ 227 | test.ok(!called,"Event not called"); 228 | conn.disconnect(); 229 | CleanUp(); 230 | test.done(); 231 | }, 1000); 232 | 233 | } 234 | 235 | exports.AddAndDeleteMultiSimple = function(test){ 236 | 237 | test.expect(2); 238 | Init(); 239 | var conn = Connect(); 240 | test.ok(conn.connected,"Connected to database"); 241 | var called = false; 242 | conn.on("fbevent", function(event,count){ 243 | //console.log("->"+event+" "+count); 244 | called = true; 245 | }); 246 | var eN = 'eventName'; 247 | conn.addFBevent(eN + 1); 248 | conn.addFBevent(eN + 2); 249 | conn.deleteFBevent(eN + 2); 250 | conn.deleteFBevent(eN + 1); 251 | GenEvent(eN + 1); 252 | 253 | setTimeout(function(){ 254 | test.ok(!called,"Event not called"); 255 | conn.disconnect(); 256 | CleanUp(); 257 | test.done(); 258 | }, 1000); 259 | 260 | } 261 | exports.AddAndDeleteMultiComplex = function(test){ 262 | 263 | test.expect(2); 264 | Init(); 265 | var conn = Connect(); 266 | test.ok(conn.connected,"Connected to database"); 267 | var called = false; 268 | conn.on("fbevent", function(event,count){ 269 | //console.log("->"+event+" "+count); 270 | called = true; 271 | }); 272 | var eN = 'eventName'; 273 | /* conn.addFBevent(eN + 1); 274 | conn.addFBevent(eN + 2); 275 | conn.addFBevent(eN + 3); 276 | conn.addFBevent(eN + 10); 277 | conn.deleteFBevent(eN + 3); 278 | conn.deleteFBevent(eN + 2); 279 | conn.deleteFBevent(eN + 10); 280 | conn.deleteFBevent(eN + 1); 281 | GenEvent(eN + 1); */ 282 | 283 | // start new 284 | conn.addFBevent(eN); 285 | var evs = [...Array(20).keys()]; 286 | for (i in evs) { 287 | conn.addFBevent(eN+i); 288 | } 289 | for (i in evs) { 290 | conn.deleteFBevent(eN+i); 291 | } 292 | 293 | conn.deleteFBevent(eN); 294 | GenEvent(eN); 295 | //end new 296 | 297 | 298 | setTimeout(function(){ 299 | test.ok(!called,"Event not called"); 300 | conn.disconnect(); 301 | CleanUp(); 302 | test.done(); 303 | }, 1000); 304 | 305 | } 306 | 307 | 308 | exports.WaitForManyFire2 = function(test){ 309 | 310 | var expected = [ 311 | { 312 | eventName: 'event1', 313 | genCount: 4, 314 | actualCount: 0 315 | }, 316 | { 317 | eventName: 'event2', 318 | genCount: 0, 319 | actualCount: 0 320 | }, 321 | { 322 | eventName: 'event3', 323 | genCount: 2, 324 | actualCount: 0 325 | } 326 | ], ecnt = expected.reduce(function(r,ev){ return r+ev.genCount;},0)+2; 327 | 328 | console.log('expected ',ecnt); 329 | test.expect(ecnt); 330 | Init(); 331 | var conn = Connect(); 332 | test.ok(conn.connected,"Connected to database"); 333 | 334 | 335 | conn.on("fbevent", function(event,count){ 336 | var found; 337 | console.log('fired event',event,count); 338 | expected.some(function(ev){ 339 | if(ev.eventName==event){ 340 | found = ev; 341 | return true; 342 | } 343 | }); 344 | test.ok(found,'event fired is known to us'); 345 | if(found) found.actualCount++; 346 | }); 347 | 348 | var sTime=100; 349 | expected.forEach(function(ev){ 350 | // wait for all events 351 | conn.addFBevent(ev.eventName); 352 | // generate events at some timeout 353 | for(var i=0;i FBblob::constructor_template; 13 | char FBblob::err_message[MAX_ERR_MSG_LEN]; 14 | 15 | void FBblob::Initialize (v8::Local target) 16 | { 17 | // HandleScope scope; 18 | 19 | Local t = Nan::New(FBblob::New); 20 | constructor_template.Reset(t); 21 | 22 | t->Inherit(Nan::New(FBEventEmitter::constructor_template)); 23 | t->SetClassName(Nan::New("FBblob").ToLocalChecked()); 24 | 25 | Local instance_template = t->InstanceTemplate(); 26 | 27 | Nan::SetPrototypeMethod(t, "_readSync", ReadSync); 28 | Nan::SetPrototypeMethod(t, "_read", Read); 29 | Nan::SetPrototypeMethod(t, "_openSync", OpenSync); 30 | Nan::SetPrototypeMethod(t, "_closeSync", CloseSync); 31 | Nan::SetPrototypeMethod(t, "_writeSync", WriteSync); 32 | Nan::SetPrototypeMethod(t, "_write", Write); 33 | 34 | 35 | instance_template->SetInternalFieldCount(1); 36 | 37 | Local instance_t = t->InstanceTemplate(); 38 | Nan::SetAccessor(instance_t, Nan::New("inAsyncCall").ToLocalChecked(),InAsyncGetter); 39 | Nan::SetAccessor(instance_t, Nan::New("isReadable").ToLocalChecked(),IsReadGetter); 40 | 41 | Nan::Set(target, Nan::New("FBblob").ToLocalChecked(), Nan::GetFunction(t).ToLocalChecked()); 42 | 43 | } 44 | 45 | bool FBblob::HasInstance(v8::Local val) 46 | { 47 | if (!val->IsObject()) return false; 48 | v8::Local obj = Nan::To(val).ToLocalChecked(); 49 | /* 50 | if (obj->GetIndexedPropertiesExternalArrayDataType() == kExternalUnsignedByteArray) 51 | return true; 52 | */ 53 | if (Nan::New(constructor_template)->HasInstance(obj)) 54 | return true; 55 | 56 | return false; 57 | } 58 | void FBblob::getId(ISC_QUAD* Idp) 59 | { 60 | *Idp = blob_id; 61 | } 62 | 63 | NAN_METHOD(FBblob::New) 64 | { 65 | 66 | Nan::HandleScope scope; 67 | 68 | ISC_QUAD *quad = NULL; 69 | Connection *conn = NULL; 70 | ISC_STATUS_ARRAY status; 71 | 72 | if((info.Length() > 0) && !info[0]->IsNull()){ 73 | REQ_EXT_ARG(0, js_quad); 74 | quad = static_cast(js_quad->Value()); 75 | } 76 | 77 | if(info.Length() > 1) { 78 | REQ_EXT_ARG(1, js_connection); 79 | conn = static_cast(js_connection->Value()); 80 | } 81 | 82 | status[1] = 0; 83 | FBblob *res = new FBblob(quad, conn->def_trans, status); 84 | if(status[1]) { 85 | Nan::ThrowError(String::Concat(FB_MAYBE_NEED_ISOLATE 86 | Nan::New("In FBblob::New - ").ToLocalChecked(), 87 | ERR_MSG_STAT(status, FBblob) 88 | )); 89 | } 90 | res->Wrap(info.This()); 91 | info.GetReturnValue().Set(info.This()); 92 | 93 | } 94 | 95 | NAN_METHOD(FBblob::ReadSync) 96 | { 97 | Nan::HandleScope scope; 98 | 99 | FBblob *blob = Nan::ObjectWrap::Unwrap(info.This()); 100 | 101 | if (!Buffer::HasInstance(info[0])) { 102 | return Nan::ThrowError("First argument needs to be a buffer"); 103 | } 104 | 105 | Local buffer_obj = Nan::To(info[0]).ToLocalChecked(); 106 | char *buffer_data = Buffer::Data(buffer_obj); 107 | size_t buffer_length = Buffer::Length(buffer_obj); 108 | if (buffer_length > USHRT_MAX) { 109 | buffer_length = USHRT_MAX; 110 | } 111 | unsigned short actual; 112 | ISC_STATUS_ARRAY status; 113 | int res = blob->read(status, buffer_data, (unsigned short) buffer_length, &actual); 114 | if(res==-1) { 115 | return Nan::ThrowError(String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("In FBblob::New - ").ToLocalChecked(),ERR_MSG_STAT(status, FBblob))); 116 | } 117 | 118 | info.GetReturnValue().Set(Nan::New(actual)); 119 | } 120 | 121 | void FBblob::EIO_After_Read(uv_work_t *req) 122 | { 123 | // uv_unref(uv_default_loop()); 124 | Nan::HandleScope scope; 125 | struct rw_request *r_req = (struct rw_request *)(req->data); 126 | delete req; 127 | Local argv[3]; 128 | int argc; 129 | 130 | 131 | if(r_req->res!=-1) 132 | { 133 | argv[0] = Nan::Null(); 134 | argv[1] = Nan::CopyBuffer(r_req->buffer,(size_t) r_req->length).ToLocalChecked(); 135 | argv[2] = (r_req->res==-2) ? Nan::True() : Nan::False(); 136 | argc = 3; 137 | } 138 | else 139 | { 140 | argv[0] = Nan::Error(*Nan::Utf8String( 141 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("FBblob::EIO_After_Read - ").ToLocalChecked(),ERR_MSG_STAT(r_req->status, FBblob)))); 142 | argc = 1; 143 | } 144 | 145 | Nan::Call(*r_req->callback, argc, argv); 146 | r_req->blob->stop_async(); 147 | r_req->blob->Unref(); 148 | free(r_req); 149 | 150 | } 151 | 152 | void FBblob::EIO_Read(uv_work_t *req) 153 | { 154 | struct rw_request *r_req = (struct rw_request *)(req->data); 155 | unsigned short actual = 0; 156 | r_req->res = r_req->blob->read(r_req->status,r_req->buffer,(unsigned short) r_req->length, &actual); 157 | r_req->length = actual; 158 | return; 159 | } 160 | 161 | NAN_METHOD(FBblob::Read) 162 | { 163 | Nan::HandleScope scope; 164 | FBblob *blob = Nan::ObjectWrap::Unwrap(info.This()); 165 | 166 | 167 | if (info.Length() != 2){ 168 | return Nan::ThrowError("Expecting 2 arguments"); 169 | } 170 | 171 | if (!Buffer::HasInstance(info[0])) { 172 | return Nan::ThrowError("First argument needs to be a buffer"); 173 | } 174 | 175 | Local buffer_obj = Nan::To(info[0]).ToLocalChecked(); 176 | char *buffer_data = Buffer::Data(buffer_obj); 177 | size_t buffer_length = Buffer::Length(buffer_obj); 178 | 179 | if(!info[1]->IsFunction()) { 180 | return Nan::ThrowError("Expecting Function as second argument"); 181 | } 182 | 183 | struct rw_request *r_req = 184 | (struct rw_request *)calloc(1, sizeof(struct rw_request)); 185 | 186 | if (!r_req) { 187 | Nan::LowMemoryNotification(); 188 | return Nan::ThrowError("Could not allocate memory."); 189 | } 190 | 191 | r_req->blob = blob; 192 | r_req->callback = new Nan::Callback(Local::Cast(info[1])); 193 | r_req->buffer = buffer_data; 194 | r_req->length = (uint32_t) buffer_length; 195 | r_req->res = 0; 196 | 197 | blob->start_async(); 198 | 199 | uv_work_t* req = new uv_work_t(); 200 | req->data = r_req; 201 | uv_queue_work(uv_default_loop(), req, EIO_Read, (uv_after_work_cb)EIO_After_Read); 202 | 203 | 204 | //uv_ref(uv_default_loop()); 205 | blob->Ref(); 206 | 207 | return; 208 | } 209 | 210 | NAN_METHOD(FBblob::OpenSync) 211 | { 212 | Nan::HandleScope scope; 213 | FBblob *blob = Nan::ObjectWrap::Unwrap(info.This()); 214 | 215 | ISC_STATUS_ARRAY status; 216 | if (!blob->open(status)) { 217 | return Nan::ThrowError( 218 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("In FBblob::_openSync - ").ToLocalChecked(), ERR_MSG_STAT(status, FBblob))); 219 | } 220 | 221 | return; 222 | } 223 | 224 | NAN_METHOD(FBblob::CloseSync) 225 | { 226 | Nan::HandleScope scope; 227 | FBblob *blob = Nan::ObjectWrap::Unwrap(info.This()); 228 | 229 | ISC_STATUS_ARRAY status; 230 | status[1] = 0; 231 | blob->close(status); 232 | if (status[1]) { 233 | return Nan::ThrowError( 234 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("In FBblob::_closeSync - ").ToLocalChecked(), ERR_MSG_STAT(status, FBblob))); 235 | } 236 | 237 | return; 238 | } 239 | 240 | NAN_METHOD(FBblob::WriteSync) 241 | { 242 | Nan::HandleScope scope; 243 | FBblob *blob = Nan::ObjectWrap::Unwrap(info.This()); 244 | ISC_STATUS_ARRAY status; 245 | 246 | 247 | if ((info.Length() > 0) && !Buffer::HasInstance(info[0])) { 248 | return Nan::ThrowError("First argument needs to be a buffer"); 249 | } 250 | 251 | Local buffer_obj = Nan::To(info[0]).ToLocalChecked(); 252 | char *buf = Buffer::Data(buffer_obj); 253 | size_t len = Buffer::Length(buffer_obj); 254 | 255 | if ((info.Length() > 1) && info[1]->IsInt32()) 256 | { 257 | size_t alen = (size_t)Nan::To(info[1]).FromJust(); 258 | if (alen < len) len = alen; 259 | } 260 | if (len > USHRT_MAX) { 261 | len = USHRT_MAX; 262 | } 263 | 264 | if (isc_put_segment(status, &blob->handle, (unsigned short)len, buf)) 265 | return Nan::ThrowError( 266 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("In FBblob::_writeSync - ").ToLocalChecked(), ERR_MSG_STAT(status, FBblob))); 267 | 268 | info.GetReturnValue().Set(Nan::New(uint32_t(len))); 269 | } 270 | 271 | void FBblob::EIO_After_Write(uv_work_t *req) 272 | { 273 | //uv_unref(uv_default_loop()); 274 | Nan::HandleScope scope; 275 | struct rw_request *w_req = (struct rw_request *)(req->data); 276 | delete req; 277 | Local argv[1]; 278 | 279 | if (w_req->callback) { 280 | 281 | if (w_req->status[1]) { 282 | argv[0] = Nan::Error(*Nan::Utf8String( 283 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("FBblob::EIO_After_Read - ").ToLocalChecked(), ERR_MSG_STAT(w_req->status, FBblob)))); 284 | } 285 | else 286 | argv[0] = Nan::Null(); 287 | 288 | Nan::Call(*w_req->callback, 1, argv); 289 | }; 290 | 291 | w_req->blob->stop_async(); 292 | w_req->blob->Unref(); 293 | free(w_req); 294 | 295 | 296 | } 297 | 298 | void FBblob::EIO_Write(uv_work_t *req) 299 | { 300 | struct rw_request *w_req = (struct rw_request *)(req->data); 301 | 302 | uint32_t remains = w_req->length; 303 | char* buf = w_req->buffer; 304 | while (remains) { 305 | unsigned short len; 306 | if (remains > USHRT_MAX) { 307 | len = USHRT_MAX / 2; 308 | } 309 | else { 310 | len = remains; 311 | } 312 | if (isc_put_segment(w_req->status, &w_req->blob->handle, len, buf)) { 313 | break; 314 | } 315 | 316 | remains -= len; 317 | buf += len; 318 | } 319 | } 320 | 321 | 322 | NAN_METHOD(FBblob::Write) 323 | { 324 | Nan::HandleScope scope; 325 | FBblob *blob = Nan::ObjectWrap::Unwrap(info.This()); 326 | 327 | if( (info.Length() > 0) && !Buffer::HasInstance(info[0])) { 328 | return Nan::ThrowError("First argument needs to be a buffer"); 329 | } 330 | 331 | Local buffer_obj = Nan::To(info[0]).ToLocalChecked(); 332 | char *buf = Buffer::Data(buffer_obj); 333 | size_t len = Buffer::Length(buffer_obj); 334 | 335 | 336 | struct rw_request *w_req = 337 | (struct rw_request *)calloc(1, sizeof(struct rw_request)); 338 | 339 | if (!w_req) { 340 | Nan::LowMemoryNotification(); 341 | return Nan::ThrowError("Could not allocate memory."); 342 | } 343 | 344 | w_req->blob = blob; 345 | w_req->buffer = buf; 346 | w_req->length = (uint32_t) len; 347 | 348 | int cb_arg = 1; 349 | if( (info.Length() > 1) && info[1]->IsInt32() ) 350 | { 351 | size_t alen = (size_t) Nan::To(info[1]).FromJust(); 352 | if(alen < len) len = alen; 353 | w_req->length = (uint32_t) len; 354 | cb_arg = 2; 355 | } 356 | 357 | if( (info.Length() > cb_arg) && info[cb_arg]->IsFunction()) { 358 | w_req->callback = new Nan::Callback(Local::Cast(info[cb_arg])); 359 | } 360 | else w_req->callback = NULL; 361 | 362 | w_req->res = 0; 363 | 364 | blob->start_async(); 365 | 366 | uv_work_t* req = new uv_work_t(); 367 | req->data = w_req; 368 | uv_queue_work(uv_default_loop(), req, EIO_Write, (uv_after_work_cb)EIO_After_Write); 369 | 370 | 371 | //uv_ref(uv_default_loop()); 372 | blob->Ref(); 373 | 374 | return; 375 | 376 | } 377 | 378 | NAN_GETTER(FBblob::IsReadGetter) 379 | { 380 | Nan::HandleScope scope; 381 | // FBblob *blob = Nan::ObjectWrap::Unwrap(info.Holder()); 382 | FBblob *blob = Nan::ObjectWrap::Unwrap(info.This()); 383 | info.GetReturnValue().Set(Nan::New(blob->is_read)); 384 | } 385 | 386 | FBblob::FBblob(ISC_QUAD *id, Transaction *atrans, ISC_STATUS *status): FBEventEmitter () 387 | { 388 | if(id) blob_id = *id; 389 | 390 | trans = atrans; 391 | is_read = true; 392 | if((id == 0) && (trans != 0)) 393 | { 394 | handle = 0; 395 | blob_id.gds_quad_high = 128; 396 | blob_id.gds_quad_low = 0; 397 | isc_create_blob2(status, &(trans->connection->db), &(trans->trans), &handle, &blob_id, 0, NULL); 398 | is_read = false; 399 | } 400 | else handle = 0; 401 | } 402 | 403 | FBblob::~FBblob() 404 | { 405 | if(handle!=0) { 406 | ISC_STATUS_ARRAY status; 407 | close(status); 408 | } 409 | } 410 | 411 | 412 | bool FBblob::open(ISC_STATUS *status) 413 | { 414 | if(isc_open_blob2(status, &(trans->connection->db), &(trans->trans), &handle, &blob_id, 0, NULL)) return false; 415 | return true; 416 | } 417 | 418 | int FBblob::read(ISC_STATUS *status, char *buf, unsigned short len, unsigned short* alen) 419 | { 420 | 421 | ISC_STATUS res = isc_get_segment(status, &handle, alen, len, buf); 422 | 423 | if(res == isc_segstr_eof) return -2; 424 | if(res != isc_segment && status[1] != 0) return -1; 425 | 426 | return 0; 427 | } 428 | 429 | bool FBblob::close(ISC_STATUS *status) 430 | { 431 | if( handle == 0 ) return true; 432 | if (isc_close_blob(status, &handle)) { 433 | return false; 434 | }; 435 | handle = 0; 436 | return true; 437 | } 438 | -------------------------------------------------------------------------------- /src/fb-bindings-eventblock.cc: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright by Denys Khanzhiyev aka xdenser 3 | * 4 | * See license text in LICENSE file 5 | */ 6 | #define BUILDING_NODE_EXTENSION 1 7 | #include 8 | #include 9 | #include "./fb-bindings-fbeventemitter.h" 10 | #include "./fb-bindings-eventblock.h" 11 | 12 | /* 13 | * Class event_block 14 | * Firebird accepts events in blocks by 15 event names. 15 | * This class helps to organize linked list chain of such blocks 16 | * to support "infinite" number of events. 17 | */ 18 | 19 | void event_block::Init() 20 | { 21 | fbevent_symbol.Reset(Nan::New("fbevent").ToLocalChecked()); 22 | } 23 | 24 | #ifdef DEBUG 25 | void event_block::dump_buf(char* buf, int len){ 26 | printf("buff dump %d: ",len); 27 | //printf("buff[0] = %d\n", buf[0]); 28 | printf("%d : [", buf[0]); 29 | buf++; len--; 30 | int c; 31 | char curr_name[127]; 32 | char* cn; 33 | while(len > 0){ 34 | c = (unsigned char) buf[0]; 35 | //printf("name len = %d\n",c); 36 | printf("%d\"", c); 37 | buf++;len--; 38 | for(cn = curr_name;c--;len--){ 39 | *cn++ = *buf++; 40 | } 41 | *cn++ = 0; 42 | //printf("event_name = %s\n",curr_name); 43 | printf("%s\": ", curr_name); 44 | //printf("count = %d\n",(uint32_t) *buf); 45 | printf("%d, ", (uint32_t)*buf); 46 | len = len - sizeof(uint32_t); 47 | buf = buf + sizeof(uint32_t); 48 | } 49 | printf("]\n"); 50 | } 51 | #endif 52 | 53 | // calculates event counts 54 | // as difference between result_buffer and event_buffer 55 | // places result in Vector 56 | void event_block::get_counts(ISC_STATUS* Vector){ 57 | char *eb, *rb; 58 | long len = blength; 59 | /* 60 | printf("result_buffer: "); 61 | dump_buf((char*)result_buffer, blength); 62 | printf("event_buffer: "); 63 | dump_buf((char*)event_buffer, blength); 64 | */ 65 | eb = (char*) event_buffer; 66 | rb = (char*) result_buffer; 67 | int idx = 0; 68 | eb++;rb++;len--; 69 | int c; 70 | int32_t vold; 71 | int32_t vnew; 72 | while(len){ 73 | c = (unsigned char) rb[0]; 74 | eb = eb + c + 1; 75 | rb = rb + c + 1; 76 | len = len - c - 1; 77 | len = len - sizeof(uint32_t); 78 | 79 | vnew = (int32_t) *rb; 80 | vold = (int32_t) *eb; 81 | //printf("o:%d,n:%d,v:%d\n", vold, vnew, (int) Vector[idx]); 82 | eb = eb + 4; 83 | rb = rb + 4; 84 | if (vold == -1 && vnew > 0) 85 | { 86 | vnew--; 87 | Vector[idx] = vnew; 88 | } 89 | else 90 | if(vnew > vold){ 91 | Vector[idx] = (vnew - vold); 92 | } 93 | else Vector[idx] = 0; 94 | // printf("o:%d,n:%d,v:%d\n", vold, vnew, (int) Vector[idx]); 95 | idx++; 96 | } 97 | memcpy(event_buffer,result_buffer,blength); 98 | } 99 | 100 | // queue event_buffer to trap event 101 | bool event_block::queue() 102 | { 103 | if(!queued) 104 | { 105 | traped = false; 106 | queued = true; 107 | 108 | if(isc_que_events( 109 | status, 110 | db, 111 | &event_id, 112 | blength, 113 | event_buffer, 114 | event_block::isc_ev_callback, 115 | (char*)this)) 116 | { 117 | queued = false; 118 | event_id = 0; 119 | return false; 120 | } 121 | } 122 | return true; 123 | } 124 | 125 | // cancel previously queued trap 126 | bool event_block::cancel() 127 | { 128 | if(queued) 129 | { 130 | traped = false; 131 | queued = false; 132 | //printf("isc_cancel_events \n"); 133 | if(isc_cancel_events(status, db, &event_id)) return false; 134 | event_id = 0; 135 | } 136 | return true; 137 | } 138 | 139 | void event_block::isc_ev_callback(void *aeb, ISC_USHORT length, const ISC_UCHAR *updated) 140 | { 141 | // this callback is called by libfbclient when event occures in FB 142 | // have not found that in documentation 143 | // but on isc_cancel_events this callback is called 144 | // with updated == NULL, length==0 145 | // here we have potential race condition 146 | // when cancel and callback get called simultanously .. hmm.. 147 | if( updated == 0 || aeb == 0 || length == 0) return; 148 | 149 | event_block* eb = static_cast(aeb); 150 | 151 | if(eb->queued) 152 | { 153 | // copy updated to result buffer 154 | // all examples use loop 155 | // why not to use mmcpy ??? 156 | 157 | 158 | if((ISC_USHORT) eb->blength < length) length = (ISC_USHORT) eb->blength; 159 | ISC_UCHAR *r = eb->result_buffer; 160 | while(length--) *r++ = *updated++; 161 | 162 | // dump_buf((char*) eb->result_buffer, eb->blength); 163 | 164 | eb->traped = true; 165 | eb->queued = false; 166 | 167 | // schedule event_notification call 168 | uv_async_send(eb->event_); 169 | } 170 | } 171 | // this is event nitification proc 172 | // here we emit node events 173 | void event_block::event_notification _UV_NOTIFICATION_SIGNATURE { 174 | Nan::HandleScope scope; 175 | ISC_STATUS_ARRAY Vector; 176 | Local argv[2]; 177 | 178 | event_block* eb = static_cast(w->data); 179 | if(eb->count==0) return; 180 | 181 | 182 | 183 | // get event counts 184 | // in ibpp they check if event was traped 185 | // we should not as if this proc was called - the event was trapped for sure 186 | // get_counts takes care about first (intialization callback call) 187 | // this is handled with proper buffer initialization with uint32_t(-1) 188 | eb->get_counts((ISC_STATUS*) Vector); 189 | 190 | int count = eb->count; 191 | int i; 192 | Local EvNames[MAX_EVENTS_PER_BLOCK]; 193 | 194 | bool emit_events = false; 195 | // collect event names to emit 196 | // do not emit here as in event callback 197 | // we may add or remove events 198 | // or even close a connection 199 | // so collect them, finish with firebird calls 200 | // and then emit 201 | for(i=0; i < count; i++){ 202 | if(Vector[i]){ 203 | EvNames[i] = Nan::New(eb->event_names[i]).ToLocalChecked(); 204 | emit_events = true; 205 | } 206 | } 207 | // requeue events 208 | if(!eb->queue()) { 209 | Nan::ThrowError( 210 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("While isc_que_events in event_notification - ").ToLocalChecked(),ERR_MSG(eb, event_block))); 211 | } 212 | 213 | // exit if block was marked as bad 214 | 215 | if(!emit_events) return; 216 | Connection *conn = eb->conn; 217 | 218 | // emit events 219 | for( i=0; i < count; i++){ 220 | if(Vector[i]) { 221 | argv[0] = EvNames[i]; 222 | argv[1] = Nan::New(uint32_t(Vector[i])); 223 | ((FBEventEmitter*) conn)->Emit(Nan::New(fbevent_symbol),2,argv); 224 | } 225 | } 226 | 227 | } 228 | 229 | 230 | void 231 | event_block::que_event(event_block *eb) 232 | { 233 | Nan::HandleScope scope; 234 | if(!eb->queue()) { 235 | Nan::ThrowError( 236 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("While isc_que_events - ").ToLocalChecked(),ERR_MSG(eb, event_block))); 237 | return ; 238 | } 239 | } 240 | 241 | int event_block::hasEvent(char *Event) 242 | { 243 | int i; 244 | for(i=0;i 255) { 255 | len = 255; 256 | } 257 | event_names[count] = (char*) calloc(1,len+1); 258 | if(!event_names[count]) return false; 259 | 260 | 261 | // copy event name 262 | char *p, *t; 263 | t = event_names[count]; 264 | for(p=Event;(*p);){ *t++ = *p++; } 265 | *t = 0; 266 | 267 | count++; 268 | // (re)allocate and fill buffer new event 269 | size_t prev_size = blength; 270 | size_t needed = ( (prev_size == 0) ? 1 : 0 ) + len + 5; 271 | event_buffer = (ISC_UCHAR*) realloc(event_buffer, prev_size + needed); 272 | if(!event_buffer) return false; 273 | result_buffer = (ISC_UCHAR*) realloc(result_buffer, prev_size + needed); 274 | if(!result_buffer) return false; 275 | if(prev_size == 0){ 276 | event_buffer[0] = result_buffer[0] = 1; 277 | } 278 | char* buf = (char*) event_buffer + ( (prev_size == 0) ? 1 : prev_size ); 279 | *(buf++) = static_cast(len); 280 | for(p=Event;*p;){ *buf++ = *p++; }; 281 | int *pi = (int*) buf; 282 | *pi = -1; 283 | //(int)(*(buf++)) = -1;// *(buf++) = -1; *(buf++) = -1; *buf = -1; 284 | size_t needed_size = prev_size + needed; 285 | if (needed_size > SHRT_MAX) { 286 | return false; 287 | } 288 | blength = (short) needed_size; 289 | memcpy(result_buffer+prev_size,event_buffer+prev_size,needed); 290 | 291 | return true; 292 | } 293 | 294 | void event_block::removeEvent(char *Event) 295 | { 296 | int idx = hasEvent(Event); 297 | if(idx<0) return; 298 | free(event_names[idx]); 299 | for(int i = idx;i < count;i++) event_names[i] = event_names[i+1]; 300 | count--; 301 | 302 | // realocate buffers 303 | char *buf = (char*) event_buffer + 1; 304 | char *rb = (char*) result_buffer + 1; 305 | while(idx) 306 | { 307 | buf = buf + (*(buf)) + 4; buf++; 308 | rb = rb + (*(rb)) + 4; rb++; 309 | idx--; 310 | } 311 | char sz = (*buf) + 5; 312 | size_t new_length = blength - sz; 313 | size_t tale_length = blength - (buf - (char*)event_buffer) - sz; 314 | if(tale_length){ 315 | memmove(buf, buf+sz, tale_length); 316 | memmove(rb, rb+sz, tale_length); 317 | } 318 | if(new_length==1) new_length = 0; 319 | 320 | event_buffer = (ISC_UCHAR*) realloc(event_buffer, new_length); 321 | result_buffer = (ISC_UCHAR*) realloc(result_buffer, new_length); 322 | blength = (short) new_length; 323 | 324 | } 325 | 326 | void 327 | event_block::RegEvent(event_block** rootp, char *Event, Connection *aconn, isc_db_handle *db) 328 | { 329 | Nan::HandleScope scope; 330 | event_block* root = *rootp; 331 | // Check if we already have registered that event 332 | event_block* res = event_block::FindBlock(root,Event); 333 | // Exit if yes 334 | if(res) return; 335 | 336 | // Search for available event block 337 | res = root; 338 | while(res) 339 | { 340 | if(res->count < MAX_EVENTS_PER_BLOCK) break; 341 | root = res; 342 | res = res->next; 343 | } 344 | // Have we found one ? 345 | if(!res){ 346 | // Create new event block 347 | //printf("create new event block\n"); 348 | res = new event_block(aconn, db); 349 | if(!res){ 350 | Nan::LowMemoryNotification(); 351 | Nan::ThrowError("Could not allocate memory."); 352 | return ; 353 | } 354 | //uv_unref((uv_handle_t*) res->event_); 355 | // uv_async_start(EV_DEFAULT_UC_ res->event_); 356 | // uv_unref(uv_default_loop()); 357 | 358 | // Set links 359 | if(root) root->next = res; 360 | res->prev = root; 361 | } 362 | 363 | // Set first element in chain if it is not set yet 364 | if(!*rootp) *rootp = res; 365 | 366 | 367 | // Cancel old queue if any 368 | if(!res->cancel()){ 369 | Nan::ThrowError( 370 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("While cancel_events - ").ToLocalChecked(),ERR_MSG(res, event_block))); 371 | return; 372 | } 373 | 374 | // Add event to block 375 | if(!res->addEvent(Event)) { 376 | Nan::LowMemoryNotification(); 377 | Nan::ThrowError("Could not allocate memory."); 378 | return ; 379 | } 380 | 381 | event_block::que_event(res); 382 | 383 | } 384 | 385 | event_block* event_block::FindBlock(event_block* root, char *Event) 386 | { 387 | // Find block with Event in linked list 388 | event_block* res = root; 389 | while(res) 390 | if(res->hasEvent(Event)==-1) res = res->next; 391 | else break; 392 | return res; 393 | } 394 | 395 | void 396 | event_block::RemoveEvent(event_block** root, char *Event) 397 | { 398 | Nan::HandleScope scope; 399 | // Find event_block with Event name 400 | event_block* eb = event_block::FindBlock( *root, Event); 401 | if(eb) 402 | { 403 | // If we have found it 404 | // it was queued and should be canceled 405 | if(!eb->cancel()){ 406 | Nan::ThrowError( 407 | String::Concat(FB_MAYBE_NEED_ISOLATE Nan::New("While cancel_events - ").ToLocalChecked(),ERR_MSG(eb, event_block))); 408 | return ; 409 | } 410 | // Remove it from event list 411 | eb->removeEvent(Event); 412 | /// if(eb->lock) return Undefined(); 413 | if(!eb->count) { 414 | // If there is no more events in block 415 | // we can free it 416 | // but keep link to next block 417 | if(eb->prev) 418 | { 419 | eb->prev->next = eb->next; 420 | } 421 | else 422 | { 423 | *root = NULL; 424 | } 425 | eb->next = NULL; 426 | delete eb; 427 | } 428 | else { 429 | // if block still has events we should requeue it 430 | return event_block::que_event(eb); 431 | } 432 | } 433 | // No block with that name 434 | // may be throw error ??? 435 | // return; 436 | } 437 | 438 | 439 | event_block::event_block(Connection *aconn,isc_db_handle *adb) 440 | { 441 | conn = aconn; 442 | db = adb; 443 | count = 0; 444 | next = NULL; 445 | prev = NULL; 446 | event_buffer = NULL; 447 | result_buffer = NULL; 448 | blength = 0; 449 | event_id = 0; 450 | event_ = new uv_async_t(); 451 | event_->data = this; 452 | uv_async_init(uv_default_loop(), event_, event_block::event_notification); 453 | 454 | queued = false; 455 | traped = false; 456 | } 457 | 458 | void close_cb(uv_handle_t* handle) { 459 | uv_async_t* event_ = (uv_async_t*)handle; 460 | delete event_; 461 | } 462 | 463 | event_block::~event_block() 464 | { 465 | cancel(); 466 | for(int i=0;i string // conversion function for text/varchar fields - this allows only to read fields, if not provided and lc_ctype is not UTF8 text fields will be returned as Buffer from FBResult 101 | } 102 | 103 | 104 | 105 | ## Connection object 106 | 107 | Handles database connection and queries. Supports Synchronous and Asynchronous operation. 108 | 109 | ### Connection object members 110 | * * * 111 | function connectSync(database, username, password, role); 112 | 113 | where 114 | 115 | * `database` - string, a database name in Firebird notation, i.e. `:` 116 | * `username` - string, user name 117 | * `pasword` - string, 118 | * `role` - string; 119 | 120 | Connects you to database, raises exception on error (try to catch it). 121 | Returns undefined. 122 | 123 | * * * 124 | function connect(database, username, password, role, callback); 125 | 126 | where first four parameters same as in connectSync() 127 | 128 | * `callback` - function(err), where err is error object in case of error. 129 | 130 | Asynchronously connects you to Database. 131 | Returns udefined. 132 | 133 | * * * 134 | function disconnect(); 135 | 136 | Dconnects from database. 137 | Returns udefined. 138 | 139 | * * * 140 | connected; 141 | A boolean readonly property indicating if Connection object is connected to database 142 | 143 | * * * 144 | function querySync(sql); 145 | * `sql` - string, an SQL query to execute. 146 | 147 | Executes SQL query. 148 | Returns FBResult object in case of success. Raises error otherwise. 149 | 150 | * * * 151 | function query(sql, callback); 152 | 153 | * `sql` - string, an SQL query to execute; 154 | * `callback` - function(err,res), err - is error object or null, res - FBResult object. 155 | 156 | Asynchronously executes query. 157 | Returns undefined. 158 | 159 | * * * 160 | function addFBevent(name); 161 | * `name` - string, Firebird Event Name. 162 | 163 | Registers connection to listen for firebird event `name`, called from PL\SQL (in stored procedures or triggers) with post_event '`name`'. 164 | You may set callback for event with `connection.on('fbevent', function(name, count){ ));`. 165 | Where name is event name, and count is number of times event were posted. 166 | 167 | * * * 168 | function deleteFBevent(name); 169 | * `name` - string, Firebird Event Name. 170 | 171 | Unsubscribes connection from getting events for name. 172 | 173 | * * * 174 | function commitSync(); 175 | 176 | Synchronously commits current transaction. 177 | 178 | Notes: 179 | There is only one transaction associated with connection. 180 | Transacation is automatically started before any query if connection does not have active transaction (check `inTransaction` property). 181 | You also should note that DDL statements (altering database structure) are commited automatically. 182 | To run quieries in context of other transaction use Transaction object. 183 | 184 | * * * 185 | function commit(callback); 186 | * `callback` - function(err), where err is error object in case of error. 187 | 188 | Asynchronous commit transaction.Read notes in `commitSync();`. 189 | 190 | * * * 191 | function rollbackSync(); 192 | 193 | Synchronously rollbacks current transaction. Read notes in `commitSync();`. 194 | 195 | * * * 196 | function rollback(callback); 197 | * `callback` - function(err), where err is error object in case of error. 198 | 199 | Asynchronously rollbacks current transaction. Read notes in `commitSync();`. 200 | 201 | * * * 202 | function startSync(); 203 | 204 | Synchronously starts new default transaction. The default transaction should be not in started state before call to this method. Read notes in `commitSync();`. 205 | 206 | * * * 207 | function start(callback); 208 | * `callback` - function(err), where err is error object in case of error. 209 | 210 | Asynchronously starts new default transaction.. Read notes in `commitSync();`. 211 | 212 | * * * 213 | function prepareSync(sql); 214 | * `sql` - string, an SQL query to prepare. 215 | 216 | Synchronously prepares SQL statement and returns FBStatement object. 217 | 218 | * * * 219 | inTransaction; 220 | 221 | A boolean readonly property indicating if connection is in started transaction state. 222 | 223 | * * * 224 | 225 | function newBlobSync(); 226 | 227 | Creates new FBblob object and opens it for write. After finishing write operation and closing blob 228 | one may insert it in database passing as parameter to exec, execSync methods of FBStatement object. 229 | 230 | * * * 231 | 232 | function startNewTransactionSync(); 233 | 234 | Creates new Transaction object and starts new transaction. Returns created object. 235 | 236 | * * * 237 | 238 | function startNewTransaction(callback); 239 | * `callback` - function(err, transaction), where err is error object in case of error, transaction - newly created transaction. 240 | 241 | Creates new Transaction object and starts new transaction. Returns created transaction object in callback. 242 | 243 | * * * 244 | 245 | ## Transaction object 246 | 247 | Represents SQL transaction. To get instance of this object call `startNewTransactionSync` or `startNewTransaction` methods of Connection object. Transaction objects may be reused after commit or rollback. 248 | 249 | * * * 250 | function querySync(sql); 251 | * `sql` - string, an SQL query to execute. 252 | 253 | Executes SQL query in context of this transaction. 254 | Returns FBResult object in case of success. Raises error otherwise. 255 | 256 | * * * 257 | function query(sql, callback); 258 | 259 | * `sql` - string, an SQL query to execute; 260 | * `callback` - function(err,res), err - is error object or null, res - FBResult object. 261 | 262 | Asynchronously executes query in context of this transaction. 263 | Returns undefined. 264 | 265 | * * * 266 | function commitSync(); 267 | 268 | Synchronously commits this transaction. 269 | 270 | Notes: 271 | Transacation is automatically started before any query in context of this object if this object does not have active transaction (check `inTransaction` property). 272 | You also should note that DDL statements (altering database structure) are commited automatically. 273 | 274 | * * * 275 | function commit(callback); 276 | * `callback` - function(err), where err is error object in case of error. 277 | 278 | Asynchronous commit transaction.Read notes in `commitSync();`. 279 | 280 | * * * 281 | function rollbackSync(); 282 | 283 | Synchronously rollbacks transaction. Read notes in `commitSync();`. 284 | 285 | * * * 286 | function rollback(callback); 287 | * `callback` - function(err), where err is error object in case of error. 288 | 289 | Asynchronously rollbacks transaction. Read notes in `commitSync();`. 290 | 291 | * * * 292 | function startSync(); 293 | 294 | Synchronously starts transaction. The transaction should be not in started state before call to this method. Read notes in `commitSync();`. See `inTransaction` property. 295 | 296 | * * * 297 | function start(callback); 298 | * `callback` - function(err), where err is error object in case of error. 299 | 300 | Asynchronously starts new transaction. Read notes in `commitSync();`. 301 | 302 | * * * 303 | function prepareSync(sql); 304 | * `sql` - string, an SQL query to prepare. 305 | 306 | Synchronously prepares SQL statement and returns FBStatement object in context of this transaction. 307 | 308 | _Note_: only prepare operation runs in context of the transaction. To execute result statement in context of this or other transaction use methods execInTrans/execInTransSync of returned statement object and pass started transaction object as argument. 309 | 310 | * * * 311 | inTransaction; 312 | 313 | A boolean readonly property indicating if this transaction is in started state. 314 | 315 | * * * 316 | ## FBResult object 317 | 318 | Represents results of SQL query if any. You should use this object to fetch rows from database. 319 | Each row may be represented as array of field values or as object with named fields. 320 | 321 | ### Data types 322 | Here is Firebird to Node data type accordance: 323 | 324 | Firebird Node 325 | DATE -> Date 326 | TIME -> Date 327 | TIMESTAMP -> Date 328 | CHAR -> String 329 | VARCHAR -> String 330 | SMALLINT -> Integer 331 | INTEGER -> Integer 332 | NUMERIC -> Number 333 | DECIMAL -> Number 334 | FLOAT -> Number 335 | DOUBLE -> Number 336 | BLOB -> FBblob 337 | 338 | 339 | ### FBResult object members 340 | 341 | * * * 342 | function fetchSync(rowCount, asObject); 343 | 344 | * `rowCount` - integer|"all", number of rows to fetch from results; 345 | * `asObject` - true|false, format of returned rows. When false - methods returns array of array, when true - array of objects. 346 | 347 | Synchronously fetches result rows. If you pass "all" as rowCount - it will fetch all result rows. 348 | If you pass less rowCount than are actually in result, it will return specified number of rows. 349 | You may call fetchSync multiple times until all rows will be fetched. 350 | If you specify more rowCount than available it will return only actual number of rows. 351 | 352 | * * * 353 | function fetch(rowCount, asObject, rowCallback, eofCallback); 354 | 355 | * `rowCount` - integer|"all", number of rows to fetch from results; 356 | * `asObject` - true|false, format of returned rows. When false - methods returns array of array, when true - array of objects; 357 | * `rowCallback` - function(row), row - Array or Object (depends on asObject parameter) representing single row from result; 358 | * `eofCallback` - function(err,eof), err - Error object in case of error, or null; eof - true | false. 359 | 360 | Asynchronously fetches rows one by one. 361 | rowCallback is called for each fetched row. 362 | eofCallback is called when whole operation is complete. eof indicates if end of result set was met. 363 | 364 | * * * 365 | ## FBStatement object 366 | 367 | Represents prepared SQL query (returned by `Connection.prepare()` and `Connection.prepareSync()`). 368 | FBStatement is derived form FBResult class. So it can fetch rows just like FBresult object after call to execSync, exec methods. 369 | 370 | ### FBStatement object members 371 | 372 | * * * 373 | function execSync(param1, param2, ..., paramN); 374 | * `param1, param2, ..., paramN` - parameters of prepared statement in the same order as in SQL and with appropriate types. 375 | 376 | Synchronously executes prepared statement with given parameters. You may fetch rows with methods inherited from FBResult. 377 | Statement is executed in context of default connection transaction. 378 | 379 | * * * 380 | function execInTransSync(transaction, param1, param2, ..., paramN); 381 | 382 | Same as `execSync` but executes statement in context of given Transaction obejct. 383 | 384 | * * * 385 | function exec(param1, param2, ..., paramN); 386 | * `param1, param2, ..., paramN` - parameters of prepared statement in the same order as in SQL and with appropriate types. 387 | 388 | Asynchronously executes prepared statement with given parameters. FBStatement emits 'result' or 'error' event. 389 | You may fetch rows with methods inherited from FBResult after 'result' event emitted. 390 | Statement is executed in context of default connection transaction. 391 | 392 | * * * 393 | function execInTrans(transaction, param1, param2, ..., paramN); 394 | 395 | Same as `exec` but executes statement in context of given Transaction obejct. 396 | 397 | * * * 398 | 399 | ## FBblob object 400 | 401 | Represents BLOB data type. 402 | 403 | ### FBblob object members 404 | 405 | * * * 406 | function _openSync(); 407 | 408 | Synchronously opens blob for reading. 409 | 410 | * * * 411 | function _closeSync(); 412 | 413 | Synchronously closes previously opened blob. 414 | 415 | * * * 416 | function _readSync(buffer); 417 | 418 | * `buffer` - Node buffer to fill with data. 419 | 420 | Synchronously reads BLOB segment (chunk) into buffer. Tries to fill whole buffer with data. 421 | Returns actual number of bytes read. 422 | 423 | * * * 424 | function _read(buffer, callback); 425 | 426 | * `buffer` - Node buffer to fill with data. 427 | * `callback` - function(err,buffer,len), err - Error object in case of error, or null;buffer - buffer filled with data; len - actual data length. 428 | 429 | Asynchronously reads BLOB segment (chunk) into buffer. Tries to fill whole buffer with data. 430 | 431 | * * * 432 | function _readAll([[initialSize], [[chunkSize], [callback]]]); 433 | 434 | * `initialSize` - optional, initial result buffer to allocate, default = 0; 435 | * `chunkSize` - optional, size of chunk used to read data, default = 1024; 436 | * `callback` - optional, function (err, buffer, len), err - Error object in case of error, or null;buffer - buffer filled with data; len - actual data length. 437 | 438 | Asynchronously reads all data from BLOB field. Object emits events while reading data `error`, `drain', `end`. 439 | 440 | * * * 441 | function _writeSync(buffer,[len]); 442 | 443 | * `buffer` - Node buffer to write from to blob; 444 | * `len` - optional length parameter, if specified only len bytes from buffer will be writen. 445 | 446 | Synchronously writes BLOB segment (chunk) from buffer. 447 | Returns number of bytes actually writen. 448 | 449 | * * * 450 | function _write(buffer,[len],[callback]); 451 | 452 | * `buffer` - Node buffer to write from to blob; 453 | * `len` - optional length parameter, if specified only len bytes from buffer will be writen. 454 | * `callback` - function(err), err - Error object in case of error, or null; 455 | 456 | Asynchronously writes BLOB segment (chunk) from buffer and calls callback function if any. 457 | 458 | * * * 459 | 460 | ## Stream object 461 | 462 | Represents BLOB stream. Create BLOB stream using `var strm = new fb.Stream(FBblob);`. 463 | You may pipe strm to/from NodeJS Stream objects (fs or socket). 464 | You may also look at [NodeJS Streams reference](http://nodejs.org/api/stream.html). 465 | 466 | 467 | [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat 468 | [license-url]: LICENSE 469 | 470 | [npm-url]: https://npmjs.org/package/firebird 471 | [npm-version-image]: http://img.shields.io/npm/v/firebird.svg?style=flat 472 | [npm-downloads-image]: http://img.shields.io/npm/dm/firebird.svg?style=flat 473 | 474 | 475 | 476 | -------------------------------------------------------------------------------- /samples/fileman/static/elfinder/css/elfinder.css: -------------------------------------------------------------------------------- 1 | 2 | /* file manager window */ 3 | 4 | .el-finder { 5 | width:100%; 6 | min-width:400px; 7 | border:1px solid #ccc; 8 | background-color:#eee; 9 | font:12px trebuchet ms,lucida grande,verdana,sans-serif; 10 | border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px; 11 | } 12 | 13 | .el-finder-undocked { 14 | position:absolute; 15 | min-width:400px; 16 | border:1px solid #ccc; 17 | padding:5px; 18 | } 19 | 20 | /* error messages */ 21 | .el-finder-err { 22 | padding: 15px; 23 | text-align:center; 24 | background: #fee; 25 | color: #cc0509; 26 | border: 2px #844 solid; 27 | border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px; 28 | } 29 | 30 | /* disabled */ 31 | .el-finder-disabled .el-finder-toolbar li, 32 | .el-finder-disabled .el-finder-nav, 33 | .el-finder-disabled .el-finder-cwd { 34 | opacity:0.35; filter:Alpha(Opacity=35); 35 | } 36 | 37 | .el-finder .el-finder-droppable { 38 | background-color:#99ccff; 39 | } 40 | .el-finder .ui-selected { 41 | background-color:#ccc; 42 | /* background-color:#c5e4f9;*/ 43 | } 44 | 45 | .el-finder input { 46 | margin:0; 47 | padding:0; 48 | outline:none; 49 | border:1px solid #ccc; 50 | } 51 | 52 | /************************************/ 53 | /* toolbar */ 54 | /************************************/ 55 | 56 | .el-finder-toolbar ul { 57 | padding:5px 7px; 58 | margin:0; 59 | list-style:none; 60 | } 61 | 62 | .el-finder-toolbar ul li { 63 | display: -moz-inline-stack; 64 | display: inline-block; 65 | zoom: 1; 66 | *display: inline; 67 | vertical-align: top; 68 | height:22px; 69 | width:23px; 70 | margin:0 2px; 71 | padding:0; 72 | background:url('../images/toolbar.png') no-repeat; 73 | border:1px solid #ccc; 74 | border-radius:3px; 75 | -moz-border-radius:3px; 76 | -webkit-border-radius:3px; 77 | } 78 | .el-finder-toolbar ul li.delim { 79 | border:none; 80 | width:3px; 81 | background-position: 1px -610px; 82 | } 83 | 84 | .el-finder-toolbar ul li.el-finder-tb-hover { 85 | border:1px solid #fff; 86 | background-color:#ccc; 87 | } 88 | 89 | .el-finder-toolbar ul li.disabled { opacity:0.35; filter:Alpha(Opacity=35); } 90 | 91 | .el-finder-toolbar ul li.back { background-position: 3px -171px; } 92 | .el-finder-toolbar ul li.reload { background-position: 3px -192px; } 93 | .el-finder-toolbar ul li.select { background-position: 3px -214px; } 94 | .el-finder-toolbar ul li.open { background-position: 4px -235px; } 95 | .el-finder-toolbar ul li.mkdir { background-position: 4px -258px; } 96 | .el-finder-toolbar ul li.mkfile { background-position: 4px -280px; } 97 | .el-finder-toolbar ul li.upload { background-position: 3px -305px; } 98 | .el-finder-toolbar ul li.rm { background-position: 3px -330px; } 99 | .el-finder-toolbar ul li.copy { background-position: 3px -356px; } 100 | .el-finder-toolbar ul li.paste { background-position: 3px -381px; } 101 | .el-finder-toolbar ul li.rename { background-position: 3px -407px; } 102 | .el-finder-toolbar ul li.edit { background-position: 4px -435px; } 103 | .el-finder-toolbar ul li.info { background-position: 3px -462px; } 104 | .el-finder-toolbar ul li.help { background-position: 3px -487px; } 105 | .el-finder-toolbar ul li.icons { background-position: 3px -537px; } 106 | .el-finder-toolbar ul li.list { background-position: 3px -557px; } 107 | .el-finder-toolbar ul li.uncompress { background-position: 3px -583px; } 108 | .el-finder-toolbar ul li.resize { background-position: 3px -647px; } 109 | .el-finder-toolbar ul li.quicklook { background-position: 3px -726px; } 110 | 111 | .el-finder-dock-button { 112 | width:19px; 113 | height:19px; 114 | float:right; 115 | margin: 2px; 116 | border:1px solid #ccc; 117 | border-radius:3px; 118 | -moz-border-radius:3px; 119 | -webkit-border-radius:3px; 120 | background:url('../images/toolbar.png') 2px -705px no-repeat; 121 | } 122 | 123 | .ui-dialog .el-finder-dock-button { 124 | background-position:2px -681px; 125 | } 126 | 127 | .el-finder-dock-button-hover { 128 | background-color:#ccc; 129 | border:1px solid #fff; 130 | } 131 | 132 | /**********************************************************/ 133 | /* workzone, container for navigation and current folder */ 134 | /**********************************************************/ 135 | 136 | .el-finder-workzone { 137 | background-color:#fff; 138 | border-top:1px solid #ccc; 139 | border-bottom:1px solid #ccc; 140 | position:relative; 141 | } 142 | 143 | .el-finder-spinner { 144 | position:absolute; 145 | top:37%; 146 | left:37%; 147 | width:250px; 148 | height:50px; 149 | background:transparent url(../images/spinner.gif) 50% 50% no-repeat; 150 | display:none; 151 | } 152 | 153 | /* error in workzone */ 154 | .el-finder-workzone p.el-finder-err { 155 | display:none; 156 | position:absolute; 157 | left:37%; 158 | top:20px; 159 | } 160 | 161 | /* navigation and current directory */ 162 | .el-finder-nav, .el-finder-cwd { 163 | height:350px; 164 | overflow:auto; 165 | padding:3px 1px; 166 | } 167 | 168 | /************************************/ 169 | /* navigation */ 170 | /************************************/ 171 | 172 | .el-finder-nav { 173 | float:left; 174 | width : 200px; 175 | background:#fff; 176 | } 177 | 178 | .el-finder-nav .ui-resizable-e { 179 | right:0; 180 | } 181 | 182 | /* folders tree */ 183 | .el-finder-nav ul { 184 | list-style:none; 185 | margin:0; 186 | padding:0; 187 | } 188 | 189 | .el-finder-nav ul li { 190 | clear:both; 191 | } 192 | 193 | ul.el-finder-tree, ul.el-finder-places { 194 | margin-bottom:1em; 195 | } 196 | 197 | .el-finder-nav ul li ul { 198 | margin-left:12px; 199 | } 200 | 201 | .el-finder-nav ul div { 202 | width:12px; 203 | height:20px; 204 | float:left; 205 | margin-right:23px; 206 | } 207 | 208 | .el-finder-nav a, .el-finder-nav div.collapsed { 209 | background-image:url(../images/toolbar.png); 210 | background-repeat:no-repeat; 211 | } 212 | .el-finder-nav div.collapsed { 213 | background-position: -1px 7px; 214 | } 215 | .el-finder-nav div.expanded { 216 | background-position: -1px -9px; 217 | } 218 | 219 | .el-finder-nav a { 220 | display: block; 221 | white-space:nowrap; 222 | line-height:20px; 223 | color:#444; 224 | cursor:default; 225 | text-decoration:none; 226 | outline:none; 227 | border-radius:3px; 228 | -moz-border-radius:3px; 229 | -webkit-border-radius:3px; 230 | background-position: 15px -56px; 231 | } 232 | 233 | .el-finder-nav a.dropbox { 234 | background-position: 15px -80px; 235 | } 236 | .el-finder-nav a.readonly { 237 | background-position: 15px -104px; 238 | } 239 | .el-finder-nav a.noaccess { 240 | background-position: 15px -750px; 241 | } 242 | 243 | .el-finder-nav a.selected { 244 | /* background-color:#ccc;*/ 245 | background-color:#c5e4f9; 246 | background-position: 15px -128px; 247 | } 248 | 249 | .el-finder-nav a.el-finder-tree-root { 250 | background-position: 15px -30px; 251 | font-weight:bold; 252 | } 253 | 254 | .el-finder-nav a.el-finder-places-root { 255 | background-position: 15px -152px; 256 | font-weight:bold; 257 | } 258 | 259 | .el-finder-nav ul.el-finder-tree .el-finder-droppable { 260 | background-position: 15px -237px; 261 | } 262 | 263 | 264 | /***********************************/ 265 | /* current working directory */ 266 | /************************************/ 267 | 268 | .el-finder-cwd { 269 | border-left:1px solid #ccc; 270 | padding:3px; 271 | } 272 | 273 | /********** view: icons ************/ 274 | .el-finder-cwd div { 275 | width: 81px; 276 | display: -moz-inline-stack; 277 | display: inline-block; 278 | vertical-align: top; 279 | zoom: 1; 280 | *display: inline; 281 | margin:0 3px 3px 0; 282 | padding:1px 0; 283 | text-align:center; 284 | border-radius:5px; 285 | -moz-border-radius:5px; 286 | -webkit-border-radius:5px; 287 | color:#000; 288 | background-color:transparent; 289 | } 290 | 291 | 292 | .el-finder-cwd p, 293 | .el-finder-ql p { 294 | width:48px; 295 | height:48px; 296 | margin:1px auto; 297 | padding:0; 298 | border-radius:5px; 299 | -moz-border-radius:5px; 300 | -webkit-border-radius:5px; 301 | background: url('../images/icons-big.png') -1px 1px no-repeat; 302 | } 303 | 304 | /* mimetypes */ 305 | 306 | .directory p { background-position: 0 -50px; } 307 | .application p,.x-java p { background-position: -1px -150px; } 308 | .audio p { background-position: -1px -300px; } 309 | .image p { background-position: -1px -250px; } 310 | .text p, .x-empty p { background-position: -1px -200px; } 311 | .video p { background-position: -1px -350px; } 312 | .vnd-adobe-photoshop p, .postscript p { background-position: 0 -250px; } 313 | /* texts */ 314 | .rtf p, .rtfd p { background-position: 0 -400px; } 315 | .html p { background-position: 0 -550px; } 316 | .css p { background-position: 0 -600px; } 317 | .javascript p, .x-javascript p { background-position: 0 -650px; } 318 | .x-perl p { background-position: 0 -700px; } 319 | .x-python p { background-position: 0 -750px; } 320 | .x-ruby p { background-position: 0 -800px; } 321 | .x-sh p, .x-shellscript p { background-position: 0 -850px; } 322 | .x-c p, .x-java-source p { background-position: 0 -900px; } 323 | .x-php p { background-position: 0 -950px; } 324 | .xml p { background-position: 0 -1000px; } 325 | /* applications */ 326 | .vnd-ms-office p, 327 | .msword p, 328 | .vnd-ms-word p, 329 | .vnd-oasis-opendocument-text p, 330 | .ms-excel p, 331 | .vnd-ms-excel p, 332 | .vnd-oasis-opendocument-spreadsheet p, 333 | .vnd-ms-powerpoint p, 334 | .vnd-oasis-opendocument-presentation p { background-position: 0 -500px; } 335 | .pdf p { background-position: 0 -450px; } 336 | .x-shockwave-flash p { background-position: 0 -1250px; } 337 | /* archives */ 338 | .zip p, .x-7z-compressed p { background-position: 0 -1050px; } 339 | .x-gzip p, .x-tar p { background-position: 0 -1100px; } 340 | .x-bzip p, .x-bzip2 p { background-position: 0 -1150px; } 341 | .x-rar p, .x-rar-compressed p { background-position: 0 -1200px; } 342 | 343 | 344 | .el-finder-cwd div.el-finder-droppable p { 345 | background-position: 0 -98px; 346 | } 347 | 348 | .el-finder-cwd label { 349 | display:block; 350 | font-size:11px; 351 | line-height:13px; 352 | padding:0 1px; 353 | margin:0; 354 | height:25px; 355 | overflow:hidden; 356 | cursor:default; 357 | } 358 | 359 | .el-finder-cwd div input { 360 | background:#fff; 361 | color:#000; 362 | width:81px; 363 | margin-left:-2px; 364 | outline:none; 365 | border:1px solid #ccc; 366 | text-align:center; 367 | } 368 | 369 | .el-finder-cwd div em { 370 | float:left; 371 | margin-top:-40px; 372 | margin-left:9px; 373 | width:15px; 374 | height:16px; 375 | background:url(../images/icons-big.png) -17px -1310px no-repeat; 376 | } 377 | 378 | .el-finder-cwd div em.dropbox { 379 | float:right; 380 | margin-right:9px; 381 | background-position: 0 -1308px; 382 | } 383 | .el-finder-cwd div em.noread { 384 | float:right; 385 | margin-right:9px; 386 | background-position: 0 -1310px; 387 | } 388 | .el-finder-cwd div em.readonly { 389 | float:right; 390 | margin-right:9px; 391 | background-position: -34px -1306px; 392 | } 393 | 394 | .el-finder-cwd div em.noaccess { 395 | float:right; 396 | margin-right:9px; 397 | background-position: 0 -1430px; 398 | } 399 | 400 | /********** view: list ************/ 401 | 402 | .el-finder-cwd table { 403 | width:100%; 404 | /* *width:99%;*/ 405 | border-collapse: collapse; 406 | border-spacing: 0; 407 | border:1px solid #ccc; 408 | border-top:0 solid; 409 | border-left:0 solid; 410 | margin:-3px -3px; 411 | } 412 | 413 | .el-finder-cwd table tr { 414 | background:transparent; 415 | } 416 | 417 | .el-finder-cwd table tr.el-finder-row-odd { 418 | background-color:#eee; 419 | } 420 | 421 | .el-finder-cwd table tr.ui-selected { 422 | background-color:#ccc; 423 | } 424 | 425 | .el-finder-cwd table th, 426 | .el-finder-cwd table td { 427 | padding:3px 5px; 428 | border-left:1px solid #ccc; 429 | cursor:default; 430 | white-space:nowrap; 431 | color:#000; 432 | 433 | } 434 | 435 | .el-finder-cwd table th { 436 | text-align:left; 437 | background:#fbf9ee; 438 | font-size:.86em; 439 | } 440 | 441 | .el-finder-cwd table td.icon { 442 | width:24px; 443 | } 444 | 445 | .el-finder-cwd table p { 446 | width:24px; 447 | height:16px; 448 | margin:0; 449 | padding:0; 450 | background:url(../images/icons-small.png) 4px 0 no-repeat; 451 | } 452 | 453 | .el-finder-cwd table .size { 454 | text-align:right; 455 | } 456 | 457 | tr.directory p { background-position:4px -16px; } 458 | tr.text p { background-position:5px -34px; } 459 | tr.image p { background-position:4px -51px; } 460 | tr.audio p { background-position:4px -70px; } 461 | tr.video p { background-position:5px -89px; } 462 | tr.application p { background-position:4px -108px; } 463 | /* text */ 464 | tr.html p { background-position:5px -188px; } 465 | tr.javascript p, 466 | tr.x-javascript p, 467 | tr.css p, 468 | tr.x-sql p, 469 | tr.xml p, 470 | tr.x-python p, 471 | tr.x-java-source p, 472 | tr.x-perl p, 473 | tr.x-ruby p { background-position:5px -228px; } 474 | tr.x-php p { background-position:5px -247px; } 475 | tr.x-c p { background-position:5px -208px; } 476 | tr.x-shellscript p, 477 | tr.x-sh p { background-position:5px -168px; } 478 | tr.rtf p, tr.rtfd p { background-position:5px -148px; } 479 | /* application */ 480 | tr.x-shockwave-flash p { background-position:4px -266px; } 481 | tr.pdf p { background-position:4px -285px; } 482 | tr.vnd-ms-office p { background-position:4px -325px; } 483 | tr.msword p, 484 | tr.vnd-oasis-opendocument-text p, 485 | tr.vnd-ms-word p { background-position:4px -346px; } 486 | tr.vnd-ms-excel p, 487 | tr.ms-excel p, 488 | tr.vnd-oasis-opendocument-spreadsheet { background-position:4px -365px; } 489 | tr.vnd-ms-powerpoint p, 490 | tr.vnd-oasis-opendocument-presentation { background-position:4px -385px; } 491 | /* archives */ 492 | tr.x-tar p, 493 | tr.x-gzip p, 494 | tr.x-bzip p, 495 | tr.x-bzip2 p, 496 | tr.zip p, 497 | tr.x-rar p, 498 | tr.x-rar-compressed p, 499 | tr.x-7z-compressed p { background-position:4px -305px; } 500 | 501 | tr.el-finder-droppable td.icon p { background-position:5px -450px; } 502 | 503 | .el-finder-cwd table td p em { 504 | float:left; 505 | width:10px; 506 | height:12px; 507 | margin-top:5px; 508 | background:url(../images/icons-small.png) 0px -405px no-repeat; 509 | } 510 | 511 | .el-finder-cwd table p em.readonly { background-position:0px -433px; } 512 | .el-finder-cwd table p em.dropbox { background-position:0px -418px; } 513 | .el-finder-cwd table p em.noread, 514 | .el-finder-cwd table p em.noaccess { background-position:0px -470px; } 515 | 516 | /************************************/ 517 | /* statusbar */ 518 | /************************************/ 519 | 520 | .el-finder-statusbar { 521 | height:20px; 522 | } 523 | 524 | .el-finder-stat, 525 | .el-finder-path, 526 | .el-finder-sel { 527 | padding:3px 9px 1px 9px; 528 | font-size:11px; 529 | color:#555; 530 | } 531 | /* current directory path */ 532 | .el-finder-path { 533 | float:left; 534 | } 535 | /* number folders/files in current directory and size */ 536 | .el-finder-stat { 537 | float:right; 538 | } 539 | /* info about selected files */ 540 | .el-finder-sel { 541 | text-align:center; 542 | } 543 | 544 | /************************************/ 545 | /* dialog window */ 546 | /************************************/ 547 | .el-finder-dialog { 548 | font-size:.84em; 549 | } 550 | .el-finder-dialog form p, .el-finder-dialog .ui-tabs p { 551 | margin:.5em; 552 | } 553 | .el-finder-dialog .ui-dialog-titlebar { 554 | padding: .2em .1em .1em .8em; 555 | } 556 | .el-finder-dialog .ui-dialog-buttonpane { 557 | padding: .1em 1em .1em .4em; 558 | font-size:.9em; 559 | } 560 | .el-finder-dialog .ui-dialog-content { 561 | padding:5px; 562 | } 563 | 564 | .el-finder-dialog hr { 565 | border:0; 566 | border-bottom: 1px #ccc solid; 567 | clear:both 568 | } 569 | .el-finder-dialog ul { 570 | margin-top:0; 571 | } 572 | 573 | .el-finder-dialog kbd { font-size:1.2em;} 574 | .el-finder-dialog a { outline: none;} 575 | 576 | .el-finder-dialog textarea { 577 | width:100%; 578 | height:400px; 579 | outline:none; 580 | border:1px solid #ccc; 581 | font-family: "Monaco", "Andale Mono", "Lucida Console", monospace; 582 | } 583 | 584 | .ui-state-error { 585 | margin: 5px 0; 586 | padding:.5em; 587 | clear:both; 588 | } 589 | 590 | .el-finder-dialog .ui-state-error .ui-icon { 591 | float: left; 592 | margin-right: .3em; 593 | } 594 | 595 | .el-finder-add-field { 596 | cursor:pointer; 597 | } 598 | 599 | .el-finder-add-field span { 600 | float:left; 601 | margin-right:.7em; 602 | } 603 | 604 | .el-finder-dialog table { 605 | width : 100%; 606 | } 607 | 608 | .el-finder-dialog table td { 609 | padding:2px 5px; 610 | 611 | } 612 | 613 | .el-finder-dialog .ui-tabs { 614 | font-size:.98em; 615 | } 616 | 617 | .el-finder-dialog .ui-tabs div { 618 | padding:0 .5em; 619 | } 620 | .el-finder-dialog .ui-tabs-nav li a { 621 | padding:.2em 1em; 622 | } 623 | 624 | /************************************/ 625 | /* contextmenu */ 626 | /************************************/ 627 | 628 | .el-finder-contextmenu { 629 | position:absolute; 630 | width:200px; 631 | background:#fff; 632 | color:#000; 633 | cursor:default; 634 | border:1px solid #ccc; 635 | border-radius:5px; 636 | -moz-border-radius:5px; 637 | -webkit-border-radius:5px; 638 | padding:5px 0; 639 | 640 | } 641 | 642 | .el-finder-contextmenu div { 643 | position:relative; 644 | display:block; 645 | margin:0; 646 | padding:4px 29px 4px 29px; 647 | white-space:nowrap; 648 | font:12px trebuchet ms,lucida grande,verdana,sans-serif; 649 | background:url('../images/toolbar.png') 0 0 no-repeat; 650 | } 651 | 652 | .el-finder-contextmenu span { 653 | float:right; 654 | width:9px; 655 | height:18px; 656 | margin-right:-27px; 657 | background:url(../images/toolbar.png) -4px 5px no-repeat; 658 | } 659 | 660 | .el-finder-contextmenu div.el-finder-contextmenu-sub { 661 | position:absolute; 662 | top:0; 663 | display:none; 664 | margin:0; 665 | padding:5px 0; 666 | background:#fff; 667 | border:1px solid #ccc; 668 | border-radius:5px; 669 | -moz-border-radius:5px; 670 | -webkit-border-radius:5px; 671 | } 672 | 673 | 674 | .el-finder-contextmenu div.reload { background-position: 5px -192px; } 675 | .el-finder-contextmenu div.select { background-position: 5px -214px; } 676 | .el-finder-contextmenu div.open { background-position: 6px -235px; } 677 | .el-finder-contextmenu div.mkdir { background-position: 6px -258px; } 678 | .el-finder-contextmenu div.mkfile { background-position: 6px -280px; } 679 | .el-finder-contextmenu div.upload { background-position: 5px -305px; } 680 | .el-finder-contextmenu div.rm { background-position: 5px -330px; } 681 | .el-finder-contextmenu div.copy { background-position: 5px -356px; } 682 | .el-finder-contextmenu div.cut { background-position: 5px -631px; } 683 | .el-finder-contextmenu div.duplicate { background-position: 5px -356px; } 684 | .el-finder-contextmenu div.paste { background-position: 5px -381px; } 685 | .el-finder-contextmenu div.rename { background-position: 5px -407px; } 686 | .el-finder-contextmenu div.edit { background-position: 6px -435px; } 687 | .el-finder-contextmenu div.info { background-position: 5px -462px; } 688 | .el-finder-contextmenu div.help { background-position: 5px -487px; } 689 | .el-finder-contextmenu div.icons { background-position: 5px -537px; } 690 | .el-finder-contextmenu div.list { background-position: 5px -557px; } 691 | .el-finder-contextmenu div.archive { background-position: 5px -583px; } 692 | .el-finder-contextmenu div.extract { background-position: 5px -583px; } 693 | .el-finder-contextmenu div.resize { background-position: 5px -655px; } 694 | .el-finder-contextmenu div.quicklook { background-position: 5px -727px; } 695 | 696 | .el-finder-contextmenu div.delim { 697 | margin:0; 698 | padding:0; 699 | height:1px; 700 | border-top:1px solid #eee; 701 | background:transparent; 702 | display:block; 703 | } 704 | .el-finder-contextmenu div.hover { background-color:#99ccff; } 705 | 706 | .el-finder-places { 707 | margin-top:.5em; 708 | } 709 | 710 | 711 | .el-finder-drag-helper { 712 | padding:0; 713 | cursor:move; 714 | zoom:1; 715 | } 716 | 717 | .el-finder-drag-helper div { 718 | border:0 solid; 719 | margin-left:-57px; 720 | 721 | } 722 | 723 | .el-finder-drag-copy { 724 | background:url('../images/toolbar.png') 0 -771px no-repeat; 725 | } 726 | 727 | .el-finder-drag-helper label { 728 | border:1px solid #ccc; 729 | background-color:#eee; 730 | border-radius:5px; 731 | -moz-border-radius:5px; 732 | -webkit-border-radius:5px; 733 | } 734 | 735 | 736 | /************************************/ 737 | /* QuickLook */ 738 | /************************************/ 739 | 740 | .el-finder-ql { 741 | position:absolute; 742 | width:420px; 743 | height:auto; 744 | padding:12px 9px; 745 | text-align:center; 746 | border-radius:9px; 747 | -moz-border-radius:9px; 748 | -webkit-border-radius:9px; 749 | background:url(../images/ql.png); 750 | overflow: inherit !important; 751 | } 752 | 753 | /* toolbar */ 754 | .el-finder-ql div.el-finder-ql-drag-handle { 755 | height:18px; 756 | font-size:14px; 757 | background-color:#777; 758 | margin:-12px -9px 12px -9px; 759 | padding:3px 0 0 19px; 760 | opacity:.8; 761 | text-align:center; 762 | white-space: nowrap; 763 | overflow:hidden; 764 | -moz-border-radius-topleft:9px; 765 | -moz-border-radius-topright:9px; 766 | -webkit-border-top-left-radius: 9px; 767 | -webkit-border-top-right-radius: 9px; 768 | border-top-left-radius: 9px; 769 | border-top-right-radius: 9px; 770 | } 771 | /* close button */ 772 | .el-finder-ql div.el-finder-ql-drag-handle span { 773 | float:left; 774 | margin:0 19px 0 -15px; 775 | } 776 | /* title in tolbar */ 777 | .el-finder-ql div.el-finder-ql-drag-handle strong { 778 | line-height:18px; 779 | margin-left:-17px; 780 | color:#fff; 781 | } 782 | 783 | .el-finder-ql div.el-finder-ql-media { 784 | width:100%; 785 | padding:0; 786 | } 787 | 788 | .el-finder-ql div.el-finder-ql-content { 789 | width:100%; 790 | font:.82em/1.3em trebuchet ms,lucida grande,verdana,sans-serif; 791 | padding:5px 0; 792 | overflow:hidden; 793 | } 794 | 795 | .el-finder-ql div.el-finder-ql-content span, 796 | .el-finder-ql div.el-finder-ql-content a { 797 | display:block; 798 | color: #fff; 799 | } 800 | 801 | /* text files preview */ 802 | .el-finder-ql iframe { 803 | background:#fff; 804 | width:100%; 805 | height:315px; 806 | padding:0; 807 | margin:0; 808 | border:none; 809 | outline:none; 810 | } 811 | 812 | 813 | /* images preview */ 814 | .el-finder-ql img { 815 | margin:0 auto; 816 | border:1px solid #fff; 817 | } 818 | 819 | /* button help */ 820 | .el-finder-help-std { 821 | background: url(../images/icons-big.png) 0 -1380px no-repeat; 822 | width:48px; 823 | height:48px; 824 | float:right; 825 | } 826 | 827 | .el-finder-logo { 828 | background: url(../images/icons-big.png) 0 -1329px no-repeat; 829 | width:48px; 830 | height:48px; 831 | float:left; 832 | } 833 | --------------------------------------------------------------------------------