├── .gitignore ├── .gitmodules ├── .npmignore ├── CHANGELOG ├── README.md ├── binding.gyp ├── db-mysql.js ├── package.json ├── src ├── connection.cc ├── connection.h ├── mysql.cc ├── mysql.h ├── mysql_bindings.cc ├── query.cc ├── query.h ├── result.cc └── result.h ├── tests-settings.json ├── tests.js └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | /build/* 2 | *.node 3 | *.sh 4 | *.swp 5 | .lock* 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/node-db"] 2 | path = lib/node-db 3 | url = git://github.com/imkira/node-db.git 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build.sh 2 | drizzle-test.js 3 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | For more in depth changelog, check the Github Commits: 2 | 3 | https://github.com/mariano/node-db-mysql/commits/master 4 | 5 | 0.7.6 6 | ----- 7 | * Fixing issue where NODE_PSYMBOL throws compilation errors on node <= 0.4.9 8 | 9 | 0.7.5 10 | ----- 11 | * Fixing issue where error message would be lost when multiple queries errored out 12 | * Adding tests to show chained JOINs usage 13 | 14 | 0.7.4 15 | ----- 16 | * Fixing issue where when on error event handler was attached, node-db fails horribly 17 | 18 | 0.7.3 19 | ----- 20 | * Fixing issue when using the three-argument call on Query::execute() 21 | 22 | 0.7.2 23 | ----- 24 | * Fixing issue where numbers that are part of a string could be treated as pure numbers 25 | 26 | 0.7.1 27 | ----- 28 | * Fixing issue with number conversions possibly failing (thanks @tojocky) 29 | 30 | 0.7.0 31 | ----- 32 | * Fixing issue where ints were treated as floating point numbers 33 | * Allowing user to change precision of floating point numbers 34 | * Fixing unit tests (they require a valid DB connection now. Check the tests-settings.json file) 35 | 36 | 0.6.9 37 | ----- 38 | * Fixing precision with big numbers 39 | 40 | 0.6.8 41 | ----- 42 | * Adding support for both node 0.4.x and 0.5.x 43 | 44 | 0.6.7 45 | ----- 46 | * Throwing exception if Query.execute() is issued without an active connection 47 | 48 | 0.6.6 49 | ----- 50 | * Fixed issue where new connections would throw SEGFAULT on some OSs 51 | 52 | 0.6.5 53 | ----- 54 | * Fixed issue where conversion of result fields to JS Date object could be truncated on some systems 55 | 56 | 0.6.4 57 | ----- 58 | * Fixed issue where Database.setReconnect() and Database.setSslVerifyServer() were causing fatal errors 59 | 60 | 0.6.3 61 | ----- 62 | * Exporting Query so its prototype can be extended from JS 63 | * Query.execute() callback now is issued in the Query instance scope 64 | 65 | 0.6.2 66 | ----- 67 | * Reconnection is enabled by default 68 | * Database.isConnected() properly pings the server to check status of connection 69 | 70 | 0.6.1 71 | ----- 72 | * Fixing issue where Query.execute(callback, options) was not easily doable 73 | 74 | 0.6.0 75 | ----- 76 | * Fixing issue where Database.name() was escaping star chars 77 | 78 | 0.5.9 79 | ----- 80 | * Fixing compilation warnings on some 32 bit systems (CentOS) 81 | 82 | 0.5.8 83 | ----- 84 | * Database.connect() now takes an error-first callback 85 | * Fixing compilation issues in CentOS 86 | 87 | 0.5.7 88 | ----- 89 | * [API CHANGE] Query.execute() callback now takes error as its first argument 90 | * Added new optional connection options: charset, compress, initCommand, readTimeout, reconnect, sslVerifyServer, timeout, writeTimeout 91 | 92 | 0.5.6 93 | ----- 94 | First public release 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # db-mysql: MySQL database bindings for Node.js # 2 | 3 | For detailed information about this and other Node.js 4 | database bindings visit the [Node.js db-mysql homepage] [homepage]. 5 | 6 | ## INSTALL ## 7 | 8 | Before proceeding with installation, you need to have the libmysql 9 | development libraries and include files. Access to the mysql_config 10 | binary is mandatory, and its path has to be specified prior to 11 | installation (if its not within the system's path). For example, if 12 | the path to your mysql_config is /usr/local/bin/mysql_config make 13 | sure to do the following (Ubuntu users need to install the 14 | libmysqlclient-dev package): 15 | 16 | ```bash 17 | $ export MYSQL_CONFIG=/usr/local/bin/mysql_config 18 | ``` 19 | 20 | Once you are sure that either mysql_config is part of the path or that 21 | you specified the MYSQL_CONFIG environment var, install with npm: 22 | 23 | ```bash 24 | $ npm install db-mysql 25 | ``` 26 | 27 | ## QUICK START ## 28 | 29 | ```javascript 30 | var mysql = require('db-mysql'); 31 | new mysql.Database({ 32 | hostname: 'localhost', 33 | user: 'root', 34 | password: 'password', 35 | database: 'node' 36 | }).connect(function(error) { 37 | if (error) { 38 | return console.log("CONNECTION ERROR: " + error); 39 | } 40 | 41 | this.query().select('*').from('users').execute(function(error, rows) { 42 | if (error) { 43 | return console.log('ERROR: ' + error); 44 | } 45 | console.log(rows.length + ' ROWS'); 46 | }); 47 | }); 48 | ``` 49 | 50 | ## LICENSE ## 51 | 52 | This module is released under the [MIT License] [license]. 53 | 54 | [homepage]: http://nodejsdb.org/db-mysql 55 | [license]: http://www.opensource.org/licenses/mit-license.php 56 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'conditions': [ 3 | ['OS=="mac"', { 4 | 'make_global_settings': [ 5 | ['CC', '/usr/bin/gcc'], 6 | ['CXX', '/usr/bin/g++'] 7 | ] 8 | }]], 9 | "targets": [ 10 | { 11 | "target_name": "mysql_bindings", 12 | 'include_dirs': [ 13 | 'lib/', 14 | '.', 15 | '=0.8.0" } 7 | , "maintainers" : 8 | [ { "name": "Mariano Iglesias" 9 | , "email": "mgiglesias@gmail.com" 10 | } 11 | ] 12 | , "bugs" : { "url" : "http://github.com/mariano/node-db-mysql/issues" } 13 | , "licenses" : [ { "type" : "MIT" } ] 14 | , "repositories" : 15 | [ { "type" : "git" 16 | , "url" : "https://github.com/mariano/node-db-mysql.git" 17 | } 18 | ] 19 | , "main" : "./db-mysql" 20 | , "scripts" : 21 | { "preinstall": "git clone git://github.com/mariano/node-db.git lib/node-db" 22 | , "install": "node-gyp rebuild --release" 23 | , "preuninstall": "rm -rf build/*" 24 | , "test" : "nodeunit tests.js" 25 | } 26 | , "devDependencies" : 27 | { "nodeunit" : "*" 28 | , "nodelint" : "*" 29 | , "node-gyp" : "*" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/connection.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mariano Iglesias 2 | #include "./connection.h" 3 | 4 | node_db_mysql::Connection::Connection() 5 | : compress(false), 6 | readTimeout(0), 7 | reconnect(true), 8 | sslVerifyServer(false), 9 | timeout(0), 10 | writeTimeout(0), 11 | connection(NULL) { 12 | this->port = 3306; 13 | } 14 | 15 | node_db_mysql::Connection::~Connection() { 16 | this->close(); 17 | } 18 | 19 | void node_db_mysql::Connection::setCharset(const std::string& charset) throw() { 20 | this->charset = charset; 21 | } 22 | 23 | void node_db_mysql::Connection::setCompress(const bool compress) throw() { 24 | this->compress = compress; 25 | } 26 | 27 | void node_db_mysql::Connection::setInitCommand(const std::string& initCommand) throw() { 28 | this->initCommand = initCommand; 29 | } 30 | 31 | void node_db_mysql::Connection::setReadTimeout(const uint32_t readTimeout) throw() { 32 | this->readTimeout = readTimeout; 33 | } 34 | 35 | void node_db_mysql::Connection::setReconnect(const bool reconnect) throw() { 36 | this->reconnect = reconnect; 37 | } 38 | 39 | void node_db_mysql::Connection::setSocket(const std::string& socket) throw() { 40 | this->socket = socket; 41 | } 42 | 43 | void node_db_mysql::Connection::setSslVerifyServer(const bool sslVerifyServer) throw() { 44 | this->sslVerifyServer = sslVerifyServer; 45 | } 46 | 47 | void node_db_mysql::Connection::setTimeout(const uint32_t timeout) throw() { 48 | this->timeout = timeout; 49 | } 50 | 51 | void node_db_mysql::Connection::setWriteTimeout(const uint32_t writeTimeout) throw() { 52 | this->writeTimeout = writeTimeout; 53 | } 54 | 55 | bool node_db_mysql::Connection::isAlive(bool ping) throw() { 56 | if (ping && this->alive) { 57 | this->alive = (mysql_ping(this->connection) == 0); 58 | } 59 | return this->alive; 60 | } 61 | 62 | void node_db_mysql::Connection::open() throw(node_db::Exception&) { 63 | this->close(); 64 | 65 | this->connection = mysql_init(NULL); 66 | if (this->connection == NULL) { 67 | throw node_db::Exception("Cannot create MYSQL handle"); 68 | } 69 | 70 | if (!this->charset.empty()) { 71 | mysql_options(this->connection, MYSQL_SET_CHARSET_NAME, this->charset.c_str()); 72 | } 73 | 74 | if (this->compress) { 75 | mysql_options(this->connection, MYSQL_OPT_COMPRESS, (const char*) 0); 76 | } 77 | 78 | if (!this->initCommand.empty()) { 79 | mysql_options(this->connection, MYSQL_INIT_COMMAND, this->initCommand.c_str()); 80 | } 81 | 82 | if (this->readTimeout > 0) { 83 | mysql_options(this->connection, MYSQL_OPT_READ_TIMEOUT, (const char*) &this->readTimeout); 84 | } 85 | 86 | #if MYSQL_VERSION_ID >= 50013 87 | mysql_options(this->connection, MYSQL_OPT_RECONNECT, (const char*) &this->reconnect); 88 | #endif 89 | 90 | mysql_options(this->connection, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char*) &this->sslVerifyServer); 91 | 92 | if (this->timeout > 0) { 93 | mysql_options(this->connection, MYSQL_OPT_CONNECT_TIMEOUT, (const char*) &this->timeout); 94 | } 95 | 96 | if (this->writeTimeout > 0) { 97 | mysql_options(this->connection, MYSQL_OPT_WRITE_TIMEOUT, (const char*) &this->writeTimeout); 98 | } 99 | 100 | this->alive = mysql_real_connect( 101 | this->connection, 102 | this->hostname.c_str(), 103 | this->user.c_str(), 104 | this->password.c_str(), 105 | this->database.c_str(), 106 | this->port, 107 | !this->socket.empty() ? this->socket.c_str() : NULL, 108 | 0); 109 | 110 | #if MYSQL_VERSION_ID >= 50013 && MYSQL_VERSION_ID < 50019 111 | // MySQL incorrectly resets the MYSQL_OPT_RECONNECT option to its default value before MySQL 5.0.19 112 | mysql_options(this->connection, MYSQL_OPT_RECONNECT, (const char*) &this->reconnect); 113 | #endif 114 | 115 | if (!this->alive) { 116 | throw node_db::Exception(mysql_error(this->connection)); 117 | } 118 | } 119 | 120 | void node_db_mysql::Connection::close() { 121 | if (this->alive) { 122 | mysql_close(this->connection); 123 | } 124 | this->alive = false; 125 | } 126 | 127 | std::string node_db_mysql::Connection::escape(const std::string& string) const throw(node_db::Exception&) { 128 | if (!this->alive) { 129 | throw node_db::Exception("Can\'t escape string without an active connection"); 130 | } 131 | 132 | char* buffer = new char[string.length() * 2 + 1]; 133 | if (buffer == NULL) { 134 | throw node_db::Exception("Can\'t create buffer to escape string"); 135 | } 136 | 137 | mysql_real_escape_string(this->connection, buffer, string.c_str(), string.length()); 138 | 139 | std::string escaped = buffer; 140 | delete [] buffer; 141 | return escaped; 142 | } 143 | 144 | std::string node_db_mysql::Connection::version() const { 145 | std::string version = mysql_get_server_info(this->connection); 146 | return version; 147 | } 148 | 149 | node_db::Result* node_db_mysql::Connection::query(const std::string& query) const throw(node_db::Exception&) { 150 | #ifdef MYSQL_NON_THREADSAFE 151 | throw node_db::Exception("This binding needs to be linked with the thread safe MySQL library libmysqlclient_r"); 152 | #endif 153 | 154 | if (mysql_real_query(this->connection, query.c_str(), query.length()) != 0) { 155 | throw node_db::Exception(mysql_error(this->connection)); 156 | } 157 | 158 | return new node_db_mysql::Result(this->connection); 159 | } 160 | -------------------------------------------------------------------------------- /src/connection.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mariano Iglesias 2 | #ifndef SRC_CONNECTION_H_ 3 | #define SRC_CONNECTION_H_ 4 | 5 | #include 6 | #include 7 | #include "./node-db/connection.h" 8 | #include "./result.h" 9 | 10 | namespace node_db_mysql { 11 | class Connection : public node_db::Connection { 12 | public: 13 | Connection(); 14 | ~Connection(); 15 | void setCharset(const std::string& charset) throw(); 16 | void setCompress(const bool compress) throw(); 17 | void setInitCommand(const std::string& initCommand) throw(); 18 | void setReadTimeout(const uint32_t readTimeout) throw(); 19 | void setReconnect(const bool reconnect) throw(); 20 | void setSocket(const std::string& socket) throw(); 21 | void setSslVerifyServer(const bool sslVerifyServer) throw(); 22 | void setTimeout(const uint32_t timeout) throw(); 23 | void setWriteTimeout(const uint32_t writeTimeout) throw(); 24 | bool isAlive(bool ping) throw(); 25 | void open() throw(node_db::Exception&); 26 | void close(); 27 | std::string escape(const std::string& string) const throw(node_db::Exception&); 28 | std::string version() const; 29 | node_db::Result* query(const std::string& query) const throw(node_db::Exception&); 30 | 31 | protected: 32 | std::string charset; 33 | bool compress; 34 | std::string initCommand; 35 | uint32_t readTimeout; 36 | bool reconnect; 37 | std::string socket; 38 | bool sslVerifyServer; 39 | uint32_t timeout; 40 | uint32_t writeTimeout; 41 | 42 | private: 43 | MYSQL* connection; 44 | }; 45 | } 46 | 47 | #endif // SRC_CONNECTION_H_ 48 | -------------------------------------------------------------------------------- /src/mysql.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mariano Iglesias 2 | #include "./mysql.h" 3 | 4 | v8::Persistent node_db_mysql::Mysql::constructorTemplate; 5 | 6 | node_db_mysql::Mysql::Mysql(): node_db::Binding() { 7 | this->connection = new node_db_mysql::Connection(); 8 | assert(this->connection); 9 | } 10 | 11 | node_db_mysql::Mysql::~Mysql() { 12 | if (this->connection != NULL) { 13 | delete this->connection; 14 | } 15 | } 16 | 17 | void node_db_mysql::Mysql::Init(v8::Handle target) { 18 | v8::HandleScope scope; 19 | 20 | v8::Local t = v8::FunctionTemplate::New(New); 21 | 22 | constructorTemplate = v8::Persistent::New(t); 23 | constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1); 24 | 25 | node_db::Binding::Init(target, constructorTemplate); 26 | 27 | target->Set(v8::String::NewSymbol("Mysql"), constructorTemplate->GetFunction()); 28 | } 29 | 30 | v8::Handle node_db_mysql::Mysql::New(const v8::Arguments& args) { 31 | v8::HandleScope scope; 32 | 33 | node_db_mysql::Mysql* binding = new node_db_mysql::Mysql(); 34 | if (binding == NULL) { 35 | THROW_EXCEPTION("Can't create client object") 36 | } 37 | 38 | if (args.Length() > 0) { 39 | ARG_CHECK_OBJECT(0, options); 40 | 41 | v8::Handle set = binding->set(args[0]->ToObject()); 42 | if (!set.IsEmpty()) { 43 | return scope.Close(set); 44 | } 45 | } 46 | 47 | binding->Wrap(args.This()); 48 | 49 | return scope.Close(args.This()); 50 | } 51 | 52 | v8::Handle node_db_mysql::Mysql::set(const v8::Local options) { 53 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_STRING(options, hostname); 54 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_STRING(options, user); 55 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_STRING(options, password); 56 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_STRING(options, database); 57 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_UINT32(options, port); 58 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_STRING(options, charset); 59 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_BOOL(options, compress); 60 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_STRING(options, initCommand); 61 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_UINT32(options, readTimeout); 62 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_BOOL(options, reconnect); 63 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_STRING(options, socket); 64 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_BOOL(options, sslVerifyServer); 65 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_UINT32(options, timeout); 66 | ARG_CHECK_OBJECT_ATTR_OPTIONAL_UINT32(options, writeTimeout); 67 | 68 | node_db_mysql::Connection* connection = static_cast(this->connection); 69 | 70 | v8::String::Utf8Value hostname(options->Get(hostname_key)->ToString()); 71 | v8::String::Utf8Value user(options->Get(user_key)->ToString()); 72 | v8::String::Utf8Value password(options->Get(password_key)->ToString()); 73 | v8::String::Utf8Value database(options->Get(database_key)->ToString()); 74 | 75 | if (options->Has(hostname_key)) { 76 | connection->setHostname(*hostname); 77 | } 78 | 79 | if (options->Has(user_key)) { 80 | connection->setUser(*user); 81 | } 82 | 83 | if (options->Has(password_key)) { 84 | connection->setPassword(*password); 85 | } 86 | 87 | if (options->Has(database_key)) { 88 | connection->setDatabase(*database); 89 | } 90 | 91 | if (options->Has(port_key)) { 92 | connection->setPort(options->Get(port_key)->ToInt32()->Value()); 93 | } 94 | 95 | if (options->Has(charset_key)) { 96 | v8::String::Utf8Value charset(options->Get(charset_key)->ToString()); 97 | connection->setCharset(*charset); 98 | } 99 | 100 | if (options->Has(compress_key)) { 101 | connection->setCompress(options->Get(compress_key)->IsTrue()); 102 | } 103 | 104 | if (options->Has(initCommand_key)) { 105 | v8::String::Utf8Value initCommand(options->Get(initCommand_key)->ToString()); 106 | connection->setInitCommand(*initCommand); 107 | } 108 | 109 | if (options->Has(readTimeout_key)) { 110 | connection->setReadTimeout(options->Get(readTimeout_key)->ToInt32()->Value()); 111 | } 112 | 113 | if (options->Has(reconnect_key)) { 114 | connection->setReconnect(options->Get(reconnect_key)->IsTrue()); 115 | } 116 | 117 | if (options->Has(socket_key)) { 118 | v8::String::Utf8Value socket(options->Get(socket_key)->ToString()); 119 | connection->setSocket(*socket); 120 | } 121 | 122 | if (options->Has(sslVerifyServer_key)) { 123 | connection->setSslVerifyServer(options->Get(sslVerifyServer_key)->IsTrue()); 124 | } 125 | 126 | if (options->Has(timeout_key)) { 127 | connection->setTimeout(options->Get(timeout_key)->ToInt32()->Value()); 128 | } 129 | 130 | if (options->Has(writeTimeout_key)) { 131 | connection->setWriteTimeout(options->Get(writeTimeout_key)->ToInt32()->Value()); 132 | } 133 | 134 | return v8::Handle(); 135 | } 136 | 137 | v8::Persistent node_db_mysql::Mysql::createQuery() const { 138 | v8::Persistent query( 139 | node_db_mysql::Query::constructorTemplate->GetFunction()->NewInstance()); 140 | return query; 141 | } 142 | -------------------------------------------------------------------------------- /src/mysql.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mariano Iglesias 2 | #ifndef SRC_MYSQL_H_ 3 | #define SRC_MYSQL_H_ 4 | 5 | #include "./node-db/node_defs.h" 6 | #include "./node-db/binding.h" 7 | #include "./connection.h" 8 | #include "./query.h" 9 | 10 | namespace node_db_mysql { 11 | class Mysql : public node_db::Binding { 12 | public: 13 | static void Init(v8::Handle target); 14 | 15 | protected: 16 | static v8::Persistent constructorTemplate; 17 | 18 | Mysql(); 19 | ~Mysql(); 20 | static v8::Handle New(const v8::Arguments& args); 21 | v8::Handle set(const v8::Local options); 22 | v8::Persistent createQuery() const; 23 | }; 24 | } 25 | 26 | #endif // SRC_MYSQL_H_ 27 | -------------------------------------------------------------------------------- /src/mysql_bindings.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mariano Iglesias 2 | #include "./node-db/binding.h" 3 | #include "./mysql.h" 4 | #include "./query.h" 5 | 6 | extern "C" { 7 | void init(v8::Handle target) { 8 | node_db::EventEmitter::Init(); 9 | node_db_mysql::Mysql::Init(target); 10 | node_db_mysql::Query::Init(target); 11 | } 12 | 13 | NODE_MODULE(mysql_bindings, init); 14 | } 15 | -------------------------------------------------------------------------------- /src/query.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mariano Iglesias 2 | #include "./query.h" 3 | 4 | v8::Persistent node_db_mysql::Query::constructorTemplate; 5 | 6 | void node_db_mysql::Query::Init(v8::Handle target) { 7 | v8::HandleScope scope; 8 | 9 | v8::Local t = v8::FunctionTemplate::New(New); 10 | 11 | constructorTemplate = v8::Persistent::New(t); 12 | constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1); 13 | 14 | node_db::Query::Init(target, constructorTemplate); 15 | 16 | target->Set(v8::String::NewSymbol("Query"), constructorTemplate->GetFunction()); 17 | } 18 | 19 | v8::Handle node_db_mysql::Query::New(const v8::Arguments& args) { 20 | v8::HandleScope scope; 21 | 22 | node_db_mysql::Query* query = new node_db_mysql::Query(); 23 | if (query == NULL) { 24 | THROW_EXCEPTION("Can't create query object") 25 | } 26 | 27 | if (args.Length() > 0) { 28 | v8::Handle set = query->set(args); 29 | if (!set.IsEmpty()) { 30 | return scope.Close(set); 31 | } 32 | } 33 | 34 | query->Wrap(args.This()); 35 | 36 | return scope.Close(args.This()); 37 | } 38 | -------------------------------------------------------------------------------- /src/query.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mariano Iglesias 2 | #ifndef SRC_QUERY_H_ 3 | #define SRC_QUERY_H_ 4 | 5 | #include "./node-db/node_defs.h" 6 | #include "./node-db/query.h" 7 | 8 | namespace node_db_mysql { 9 | class Query : public node_db::Query { 10 | public: 11 | static v8::Persistent constructorTemplate; 12 | static void Init(v8::Handle target); 13 | 14 | protected: 15 | static v8::Handle New(const v8::Arguments& args); 16 | }; 17 | } 18 | 19 | #endif // SRC_QUERY_H_ 20 | -------------------------------------------------------------------------------- /src/result.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mariano Iglesias 2 | #include "./result.h" 3 | 4 | node_db_mysql::Result::Column::Column(const MYSQL_FIELD& column) { 5 | this->binary = column.flags & BINARY_FLAG; 6 | this->name = column.name; 7 | 8 | switch (column.type) { 9 | case MYSQL_TYPE_TINY: 10 | this->type = (column.length == 1 ? BOOL : INT); 11 | break; 12 | case MYSQL_TYPE_BIT: 13 | case MYSQL_TYPE_SHORT: 14 | case MYSQL_TYPE_YEAR: 15 | case MYSQL_TYPE_INT24: 16 | case MYSQL_TYPE_LONG: 17 | this->type = INT; 18 | break; 19 | case MYSQL_TYPE_LONGLONG: 20 | this->type = BIGINT; 21 | break; 22 | case MYSQL_TYPE_FLOAT: 23 | case MYSQL_TYPE_DOUBLE: 24 | case MYSQL_TYPE_DECIMAL: 25 | case MYSQL_TYPE_NEWDECIMAL: 26 | this->type = NUMBER; 27 | break; 28 | case MYSQL_TYPE_DATE: 29 | this->type = DATE; 30 | break; 31 | case MYSQL_TYPE_TIME: 32 | this->type = TIME; 33 | break; 34 | case MYSQL_TYPE_TIMESTAMP: 35 | case MYSQL_TYPE_DATETIME: 36 | this->type = DATETIME; 37 | break; 38 | case MYSQL_TYPE_TINY_BLOB: 39 | case MYSQL_TYPE_MEDIUM_BLOB: 40 | case MYSQL_TYPE_LONG_BLOB: 41 | case MYSQL_TYPE_BLOB: 42 | this->type = TEXT; 43 | break; 44 | case MYSQL_TYPE_STRING: 45 | case MYSQL_TYPE_VAR_STRING: 46 | this->type = this->binary ? TEXT : STRING; 47 | break; 48 | case MYSQL_TYPE_SET: 49 | this->type = SET; 50 | break; 51 | default: 52 | this->type = STRING; 53 | break; 54 | } 55 | } 56 | 57 | node_db_mysql::Result::Column::~Column() { 58 | } 59 | 60 | bool node_db_mysql::Result::Column::isBinary() const { 61 | return this->binary; 62 | } 63 | 64 | std::string node_db_mysql::Result::Column::getName() const { 65 | return this->name; 66 | } 67 | 68 | node_db::Result::Column::type_t node_db_mysql::Result::Column::getType() const { 69 | return this->type; 70 | } 71 | 72 | node_db_mysql::Result::Result(MYSQL* connection) throw(node_db::Exception&) 73 | : columns(NULL), 74 | totalColumns(0), 75 | rowNumber(0), 76 | empty(true), 77 | connection(connection), 78 | previousRow(NULL), 79 | nextRow(NULL) { 80 | this->result = mysql_store_result(this->connection); 81 | 82 | MYSQL_RES* tmp_result; 83 | while (mysql_more_results(this->connection)) { 84 | if (mysql_next_result(this->connection)) { 85 | tmp_result = mysql_use_result(this->connection); 86 | mysql_free_result(tmp_result); 87 | } 88 | } 89 | tmp_result = NULL; 90 | 91 | try { 92 | if (result == NULL && mysql_field_count(this->connection) != 0) { 93 | throw node_db::Exception(mysql_error(this->connection)); 94 | } else if (result != NULL) { 95 | this->empty = false; 96 | 97 | MYSQL_FIELD* fields = mysql_fetch_fields(this->result); 98 | if (fields == NULL) { 99 | throw node_db::Exception("Could not buffer columns"); 100 | } 101 | 102 | this->totalColumns = mysql_num_fields(this->result); 103 | if (this->totalColumns > 0) { 104 | this->columns = new Column*[this->totalColumns]; 105 | if (this->columns == NULL) { 106 | throw node_db::Exception("Could not allocate storage for columns"); 107 | } 108 | 109 | for (uint16_t i = 0; i < this->totalColumns; i++) { 110 | this->columns[i] = new Column(fields[i]); 111 | if (this->columns[i] == NULL) { 112 | this->totalColumns = i; 113 | throw node_db::Exception("Could not allocate storage for column"); 114 | } 115 | } 116 | } 117 | 118 | this->nextRow = this->row(); 119 | } 120 | } catch(...) { 121 | this->free(); 122 | throw; 123 | } 124 | } 125 | 126 | node_db_mysql::Result::~Result() { 127 | this->free(); 128 | } 129 | 130 | void node_db_mysql::Result::free() throw() { 131 | this->release(); 132 | 133 | if (this->columns != NULL) { 134 | for (uint16_t i = 0; i < this->totalColumns; i++) { 135 | delete this->columns[i]; 136 | } 137 | delete [] this->columns; 138 | } 139 | } 140 | 141 | void node_db_mysql::Result::release() throw() { 142 | if (this->result != NULL) { 143 | mysql_free_result(this->result); 144 | this->result = NULL; 145 | } 146 | } 147 | 148 | bool node_db_mysql::Result::hasNext() const throw() { 149 | return (this->nextRow != NULL); 150 | } 151 | 152 | char** node_db_mysql::Result::next() throw(node_db::Exception&) { 153 | if (this->nextRow == NULL) { 154 | return NULL; 155 | } 156 | 157 | this->rowNumber++; 158 | this->previousRow = this->nextRow; 159 | this->nextRow = this->row(); 160 | 161 | return this->previousRow; 162 | } 163 | 164 | unsigned long* node_db_mysql::Result::columnLengths() throw(node_db::Exception&) { 165 | return mysql_fetch_lengths(this->result); 166 | } 167 | 168 | char** node_db_mysql::Result::row() throw(node_db::Exception&) { 169 | return mysql_fetch_row(this->result); 170 | } 171 | 172 | uint64_t node_db_mysql::Result::index() const throw(std::out_of_range&) { 173 | if (this->rowNumber == 0) { 174 | throw std::out_of_range("Not standing on a row"); 175 | } 176 | return (this->rowNumber - 1); 177 | } 178 | 179 | node_db_mysql::Result::Column* node_db_mysql::Result::column(uint16_t i) const throw(std::out_of_range&) { 180 | if (i >= this->totalColumns) { 181 | throw std::out_of_range("Wrong column index"); 182 | } 183 | return this->columns[i]; 184 | } 185 | 186 | uint64_t node_db_mysql::Result::insertId() const throw() { 187 | return mysql_insert_id(this->connection); 188 | } 189 | 190 | uint64_t node_db_mysql::Result::affectedCount() const throw() { 191 | return mysql_affected_rows(this->connection); 192 | } 193 | 194 | uint16_t node_db_mysql::Result::warningCount() const throw() { 195 | return mysql_warning_count(this->connection); 196 | } 197 | 198 | uint16_t node_db_mysql::Result::columnCount() const throw() { 199 | return this->totalColumns; 200 | } 201 | 202 | uint64_t node_db_mysql::Result::count() const throw(node_db::Exception&) { 203 | if (!this->isBuffered()) { 204 | throw node_db::Exception("Result is not buffered"); 205 | } 206 | return mysql_num_rows(this->result); 207 | } 208 | 209 | bool node_db_mysql::Result::isBuffered() const throw() { 210 | return (!this->result->handle || this->result->handle->status != MYSQL_STATUS_USE_RESULT); 211 | } 212 | 213 | bool node_db_mysql::Result::isEmpty() const throw() { 214 | return this->empty; 215 | } 216 | -------------------------------------------------------------------------------- /src/result.h: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Mariano Iglesias 2 | #ifndef SRC_RESULT_H_ 3 | #define SRC_RESULT_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | #include "./node-db/exception.h" 9 | #include "./node-db/result.h" 10 | 11 | namespace node_db_mysql { 12 | class Result : public node_db::Result { 13 | public: 14 | class Column : public node_db::Result::Column { 15 | public: 16 | explicit Column(const MYSQL_FIELD& column); 17 | ~Column(); 18 | bool isBinary() const; 19 | std::string getName() const; 20 | node_db::Result::Column::type_t getType() const; 21 | 22 | protected: 23 | std::string name; 24 | type_t type; 25 | bool binary; 26 | }; 27 | 28 | explicit Result(MYSQL* connection) throw(node_db::Exception&); 29 | ~Result(); 30 | void release() throw(); 31 | bool hasNext() const throw(); 32 | char** next() throw(node_db::Exception&); 33 | unsigned long* columnLengths() throw(node_db::Exception&); 34 | uint64_t index() const throw(std::out_of_range&); 35 | Column* column(uint16_t i) const throw(std::out_of_range&); 36 | uint64_t insertId() const throw(); 37 | uint16_t columnCount() const throw(); 38 | uint64_t affectedCount() const throw(); 39 | uint16_t warningCount() const throw(); 40 | uint64_t count() const throw(node_db::Exception&); 41 | bool isBuffered() const throw(); 42 | bool isEmpty() const throw(); 43 | 44 | protected: 45 | Column** columns; 46 | uint16_t totalColumns; 47 | uint64_t rowNumber; 48 | bool empty; 49 | 50 | char** row() throw(node_db::Exception&); 51 | void free() throw(); 52 | 53 | private: 54 | MYSQL* connection; 55 | MYSQL_RES* result; 56 | char** previousRow; 57 | char** nextRow; 58 | }; 59 | } 60 | 61 | #endif // SRC_RESULT_H_ 62 | -------------------------------------------------------------------------------- /tests-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "localhost", 3 | "user": "root", 4 | "password": "password", 5 | "database": "node" 6 | } 7 | -------------------------------------------------------------------------------- /tests.js: -------------------------------------------------------------------------------- 1 | /* Escape & Query building tests */ 2 | 3 | var settings = JSON.parse(require('fs').readFileSync('./tests-settings.json','utf8')); 4 | 5 | var mysql = require("./db-mysql"); 6 | var tests = require("./lib/node-db/tests.js").get(function(callback) { 7 | new mysql.Database(settings).connect(function(err) { 8 | if (err) { 9 | throw new Error('Could not connect to test DB'); 10 | } 11 | callback(this); 12 | }); 13 | }); 14 | 15 | for(var test in tests) { 16 | exports[test] = tests[test]; 17 | } 18 | -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | #### 2 | # Copyright by Mariano Iglesias 3 | # See contributors list in README.md 4 | # 5 | # See license text in LICENSE file 6 | #### 7 | 8 | import Options, Utils 9 | from os import unlink, symlink, chdir 10 | from os.path import exists 11 | 12 | srcdir = "." 13 | blddir = "build" 14 | VERSION = "0.7.6" 15 | 16 | def set_options(opt): 17 | opt.tool_options("compiler_cxx") 18 | opt.add_option('--debug', action='store_true', help='Run tests with nodeunit_g') 19 | opt.add_option('--warn', action='store_true', help='Enable extra -W* compiler flags') 20 | 21 | def configure(conf): 22 | conf.check_tool("compiler_cxx") 23 | conf.check_tool("node_addon") 24 | 25 | # Enables all the warnings that are easy to avoid 26 | conf.env.append_unique('CXXFLAGS', ["-Wall"]) 27 | if Options.options.warn: 28 | # Extra warnings 29 | conf.env.append_unique('CXXFLAGS', ["-Wextra"]) 30 | # Extra warnings, gcc 4.4 31 | conf.env.append_unique('CXXFLAGS', ["-Wconversion", "-Wshadow", "-Wsign-conversion", "-Wunreachable-code", "-Wredundant-decls", "-Wcast-qual"]) 32 | 33 | # MySQL flags and libraries 34 | mysql_config = conf.find_program('mysql_config', var='MYSQL_CONFIG', mandatory=True) 35 | 36 | conf.env.append_unique('CXXFLAGS', Utils.cmd_output(mysql_config + ' --include').split()) 37 | conf.env.append_unique('LINKFLAGS', Utils.cmd_output(mysql_config + ' --libs_r').split()) 38 | conf.check_cxx(lib="mysqlclient_r", errmsg="Missing libmysqlclient_r") 39 | 40 | def build(bld): 41 | obj = bld.new_task_gen("cxx", "shlib", "node_addon") 42 | obj.target = "mysql_bindings" 43 | obj.source = "lib/node-db/binding.cc lib/node-db/connection.cc lib/node-db/events.cc lib/node-db/exception.cc lib/node-db/query.cc lib/node-db/result.cc src/connection.cc src/mysql.cc src/query.cc src/result.cc src/mysql_bindings.cc" 44 | obj.includes = "lib/" 45 | obj.uselib = "MYSQLCLIENT" 46 | 47 | def test(tst): 48 | test_binary = 'nodeunit' 49 | if Options.options.debug: 50 | test_binary = 'nodeunit_g' 51 | 52 | Utils.exec_command(test_binary + ' tests.js') 53 | 54 | def lint(lnt): 55 | # Bindings C++ source code 56 | print("Run CPPLint:") 57 | Utils.exec_command('cpplint --filter=-whitespace/line_length ./lib/node-db/*.h ./lib/node-db/*.cc ./src/*.h ./src/*.cc') 58 | # Bindings javascript code, and tools 59 | print("Run Nodelint for sources:") 60 | Utils.exec_command('nodelint ./package.json ./db-drizzle.js') 61 | --------------------------------------------------------------------------------