![](https://habrastorage.org/storage2/f0d/9bb/b9c/f0d9bbb9c0d4c82e6c10e3f3fbb4e5a6.png)
ã¯ããã«
ç§ã®æèŠã§ã¯ãå€ãã®äººããã§ã«Node.jsãè©ŠããŠããŸããããã¯ãå¹ åºãã¿ã¹ã¯ã解決ããããã®éåžžã«äŸ¿å©ãªããŒã«ã§ãã ãŸããNode.jsã®é åã¯ãJavaScriptã³ãŒããšãé »ç¹ã«çºçããã¿ã¹ã¯ã解決ããããã®å€æ°ã®çµã¿èŸŒã¿ã¢ãžã¥ãŒã«ãäœæã§ããããšã§ãã æšæºããã±ãŒãžã«ãªããã®ãããå Žåã¯ã npmjs.orgãªããžããªã«å€æ°ã®è¿œå ã¢ãžã¥ãŒã«ãèŠã€ããããšãã§ããŸã
ãã ããããã«ãããã¹ãŠã®ãã®ãåžæã©ããã«æ©èœãããæ©èœããªãããŸãã¯ç¹å®ã®æ¡ä»¶äžã§ãŸã£ããæ©èœããªãããŸãã¯ãã¹ãŠãããäžè¬çã§ããå ŽåããããŸããç¹å®ã®ã±ãŒã¹ã«å¿ èŠãªãã®ãåã«æ¬ èœããŠããŸãã MySQLãžã®ã¯ãšãªãåæçã«å®è¡ã§ããã¢ãžã¥ãŒã«ãš4çªç®ã®ããŒãžã§ã³ãå¿ èŠã§ããã æåã«ãã¹ããããã¢ãžã¥ãŒã«ã¯ã5çªç®ã®ããŒãžã§ã³ãšæä»çã«æ©èœããŸããããåŸã§ä»ã®ã¢ãžã¥ãŒã«ããããŸãããããªã¯ãšã¹ããåæçã«å®è¡ã§ããã¢ãžã¥ãŒã«ãèŠã€ããããšã¯ã§ããŸããã§ããã
ããã¥ã¡ã³ããå匷ããåŸãC ++ã§å¿ èŠãªã¢ãžã¥ãŒã«ãèšè¿°ããnode.jsã®ã¢ããªã³ãšããŠèšèšã§ãããšããçµè«ã«éããŸãããã¢ãžã¥ãŒã«ã®äœæããã»ã¹ã«æ £ãããå Žåã¯ãcatã«ããããã
ããŒã«
Linuxã§ã¢ãžã¥ãŒã«ãäœæããŸãã å¿ èŠãªããŒã«ããïŒ
- mysql
- ããŒã
- node-gyp
- Gcc
ããããã¹ãŠãã£ã¹ããªãã¥ãŒã·ã§ã³ã«ã©ã®ããã«ã€ã³ã¹ããŒã«ããããã¯ãnode-gypãé€ããããŒãã®ã€ã³ã¹ããŒã«ã«ä»å±ããnpmãä»ããŠã€ã³ã¹ããŒã«ãããããšãèªåã§ç¥ã£ãŠããå¿ èŠããããŸãã
npm install node-gyp
ã¢ãžã¥ãŒã«ãæžã
ãããã£ãŠãnode.jsã¯ãGoogle V8ã®JavaScriptãšã³ãžã³ã§æ§ç¯ããããã©ãããã©ãŒã ã§ãã ãããã£ãŠããã®ã¢ãžã¥ãŒã«ã§ã¯ãV8ãšã³ãžã³ã®ãªããžã§ã¯ããåŠçããå¿ èŠããããŸãã ç§ã®äººçãåçŽåããããã«ããã®ãšã³ãžã³ã®ãªããžã§ã¯ãçšã®ããŒãã·ãŒããçšæããããšããå§ãããŸãããããéžæããŸãã ããŸããMySQLã䜿çšããããã®Cé¢æ°ãžã®åç §ãéžæããŸãã ã
ã¢ãžã¥ãŒã«ã¯äœããå§ãŸããŸããïŒ
ãŸããå¿ èŠãªããããŒãèšè¿°ããŠåå空éãèšå®ããåŸããã¡ã€ã«ãäœæããå¿ èŠããããŸããç§ã®å Žåã¯mysql_sync.ccã§ãã
#include <node.h> #include <node_buffer.h> #include <v8.h> #include <mysql.h> using namespace v8;
node.jsã®ã¢ãžã¥ãŒã«ã®æäœã¯NODE_MODULEãã¯ãã®å®è¡ããå§ãŸããŸãããã®ãã¯ãã«ã¯ãã¢ãžã¥ãŒã«åãšãã¢ãžã¥ãŒã«ãæ¥ç¶ããããšãã«å®è¡ãããé¢æ°ã®ååãæž¡ãããŸãã
void init(Handle<Object> target) { target->Set(String::NewSymbol("create"), FunctionTemplate::New(create)->GetFunction()); } NODE_MODULE(mysql_sync, init)
ãããã£ãŠã inité¢æ°ã¯äœãè¿ããŸãããããªããžã§ã¯ããæž¡ãããŸãã Setã¡ãœããã䜿çšããŠcreateãšããååã§ãã®ãªããžã§ã¯ãã«ããããã£ãè¿œå ããŸããååã¯V8 Stringã¯ã©ã¹ã®ãªããžã§ã¯ãã§ãããç®çã®è¡ãæž¡ãéçé¢æ°NewSymbolã䜿çšããŠäœæãããŸãã ãã®ããããã£ã®å€ã¯ã createãšããååã®é¢æ°ããäœæãããé¢æ°ã«ãªããŸã ã
ãã¹ãŠãéåžžã«åçŽã§ããã1ã€ãããŸããããã®é¢æ°ã¯ã¢ãžã¥ãŒã«ã®æåã®ããŒãæã«1åã ãåŒã³åºããããã®åŸããŒãã¯inité¢æ°ã®åºåã§åä¿¡ãããªããžã§ã¯ãããã£ãã·ã¥ããå床åŒã³åºããŸããã
ããã§createé¢æ°ãè¿œå ããå Žåãã¢ãžã¥ãŒã«ãã³ã³ãã€ã«ããããŒãã§æ¬¡ã®ã³ãŒããå®è¡ããŸã
console.log(require('./mysql_sync.node'));
ãã®çµæãç»é¢ã«æ¬¡ã®çµæã衚瀺ãããŸãã
{ create: [Function] }
æåã®ã¹ããŒãžã®æºåãã§ããŸããã äœææ©èœã«é²ã¿ãŸãã
ã¢ãžã¥ãŒã«ã®ãªããžã§ã¯ããäœæãã
createé¢æ°ã®ã³ãŒããåãã§ãã
Handle<Value> create(const Arguments& args) { HandleScope scope; Handle<Object> ret = Object::New(); node::Buffer *buf; buf = node::Buffer::New((char *)mysql_init(NULL), sizeof(MYSQL)); ret->SetHiddenValue(String::NewSymbol("MYSQL"), buf->handle_); ret->SetHiddenValue(String::NewSymbol("connected"), Boolean::New(0)); ret->Set(String::NewSymbol("connect"), FunctionTemplate::New(connect)->GetFunction()); ret->Set(String::NewSymbol("query"), FunctionTemplate::New(query)->GetFunction()); return scope.Close(ret); }
ãã®ã³ãŒãã®æ»ãå€ã¯ãã¯ã©ã¹V8 Valueã®ãªããžã§ã¯ãã§ãã ãã®ã¯ã©ã¹ã¯ãJavaScriptããåŒã³åºãããå ŽåãC ++ãšåæ§ã«ãã¹ãŠã®JavaScripté¢æ°ãè¿ããŸãã é¢æ°ã«ãã£ãŠè¿ããããªããžã§ã¯ãã®ããããã£ãæ ŒçŽããæ°ããretãªããžã§ã¯ããäœæããŸãã ããã§ãã§ããã°MYSQLæ§é äœãžã®ãã€ã³ã¿ãŒãåæåããŸããããã¯ãä»ã®MySQLé¢æ°ãšé£æºããŠãªããžã§ã¯ãã«äœããã®æ¹æ³ã§æ ŒçŽããããã«å¿ èŠã§ãã èŠã€ãã£ããã¹ãŠã®äžã§ãnode.jsèªäœã«èšè¿°ãããŠããBufferãªããžã§ã¯ãã¯ãæ§é ãä¿åããã®ã«é©ããŠããŸããã ããŒã::ãããã¡ãŒ::æ°ããã³ã³ã¹ãã©ã¯ãã䜿çšããŠãå¿ èŠãªãµã€ãºã®æ°ãããªããžã§ã¯ããäœæããããã«MYSQLåæåæ§é ãé 眮ããŸãïŒè¿ãããçµæã確èªããã®ã¯ãããšæããŸãããå床è€éã«ããããªããããäžéšã®ãã§ãã¯ã¯çç¥ããŸãïŒ ã
ãªããžã§ã¯ãã«MYSQLãä¿åãããããŠãŒã¶ãŒã«ã¢ã¯ã»ã¹ãèš±å¯ããªãããã«ããªããžã§ã¯ãã®é衚瀺ãã£ãŒã«ãã«æ§é ãä¿åãããªãã·ã§ã³ã¯SetHiddenValueã¡ãœããã䜿çšããŠéžæãããŸãããããã¯ãéåžžã®ããããã£ã§ã¯ãªããé衚瀺ããããã£ãäœæãããšããç¹ãé€ããŠãSetã¡ãœããã«å®å šã«äŒŒãŠããŸãã JavaScriptã³ãŒãããã¢ã¯ã»ã¹ã§ããŸããã ãŸãã æ¥ç¶ãããããããã£ãé衚瀺ãã£ãŒã«ãã«ä¿åããŸããããã¯åŸã§åœ¹ç«ã¡ãŸãããä»åºŠã¯V8ããŒã«ãªããžã§ã¯ããïŒå€Falseã§ïŒé 眮ããŸãã ããã«2ã€ã®é¢æ°ã connectãšqueryãè¿œå ããåŸã ãããŠæåŸã«ãscope.CloseïŒretïŒã䜿çšããŠé¢æ°ãåŒã³åºãããªããžã§ã¯ããè¿ããŸãã
æåŸã«ãæ°ãããªããžã§ã¯ããäœæããé¢æ°ãååŸãããã®ãªããžã§ã¯ãã®ãµãŒãã¹ããŒã¿ãå«ã2ã€ã®é衚瀺ããããã£ãšãå¿ èŠãªé¢æ°ãæ ŒçŽãã2ã€ã®ãããªãã¯ããããã£ãè¿œå ããŸãã
æå®ãã2ã€ã®é¢æ°ã§ã¹ã¿ããäœæããæå®ããã³ãŒããå®è¡ããå ŽåïŒ
console.log(require('./mysql_sync.node').create());
ãã®åŸã次ã®çµæãåŸãããŸãã
{ connect: [Function], query: [Function]}
ã¢ãžã¥ãŒã«ã®ã¡ãœãã
次ã«ãã¢ãžã¥ãŒã«ã®ã¡ãœããã«ã€ããŠèª¬æããŸãã
æ¥ç¶æ¹æ³ïŒ
Handle<Value> connect(const Arguments& args) { HandleScope scope; Handle<Object> ret = Object::New(); Handle<Object> err = Object::New(); MYSQL *mysql; bool ok=true; mysql = (MYSQL *)args.Holder()->GetHiddenValue(String::NewSymbol("MYSQL"))-> ToObject()->GetIndexedPropertiesExternalArrayData(); if(args.Length()==4){ for(int i=0; i<4; i++) if(!args[i]->IsString()) ok=false; } else { ok=false; } if(ok == true){ String::AsciiValue host(args[0]->ToString()); String::AsciiValue user(args[1]->ToString()); String::AsciiValue pass(args[2]->ToString()); String::AsciiValue db(args[3]->ToString()); mysql_real_connect(mysql, *host, *user, *pass, *db, 0, NULL, 0); args.Holder()->SetHiddenValue(String::NewSymbol("connected"), Boolean::New(1)); err->Set(String::NewSymbol("id"), Uint32::New(mysql_errno(mysql))); err->Set(String::NewSymbol("text"), String::NewSymbol(mysql_error(mysql))); } else { err->Set(String::NewSymbol("id"), Uint32::New(65535)); err->Set(String::NewSymbol("text"), String::NewSymbol("Incorect parametrs of function")); } ret->Set(String::NewSymbol("err"), err); return scope.Close(ret); }
ããããç§ã¯æåã®é£ããã«åºããããŸããããã®ã¡ãœãããåŒã³åºãããªããžã§ã¯ãã¯æããã«äžããããŠãããããªããžã§ã¯ãã«ã¯ãããªãäœæ¥ã«å¿ èŠãª2ã€ã®é ããã£ãŒã«ããå«ãŸããŠããŸãã ãã ããé¢æ°ãžã®åŒæ°ã¯V8 Argumentsãªããžã§ã¯ãã«ãã£ãŠæž¡ãããŸãããã®èª¬æã詳ãã調ã¹ãŠã¿ããšãæž¡ããããªããžã§ã¯ããžã®åç §ãæ ŒçŽãããŠããããšãããããŸãã ãããååŸããã«ã¯ã HolderïŒïŒã¡ãœããã䜿çšããŸãããã®åŸã MYSQLæ§é ãæã€é ããã£ãŒã«ããååŸããGetIndexedPropertiesExternalArrayDataïŒïŒã¡ãœããã䜿çšããŠãæ§é èªäœãžã®ãã€ã³ã¿ãååŸããŸãã ã³ãŒãã«ã¯ãã以äžæ³šç®ãã¹ããã®ã¯ãããŸãã;æž¡ããããã©ã¡ãŒã¿ãŒã®æ°ãšã¿ã€ãããã§ãã¯ããŸãã ãã¹ãŠãmysql_real_connectïŒïŒé¢æ°ãšåŒã°ããŠããå Žåãmysqlãšã©ãŒãçºçãããšã©ãŒãªããžã§ã¯ããäœæãããã£ãŒã«ãå€ãšããŠãšã©ãŒãè¿œå ããŸãã ãã©ã¡ãŒã¿ãŒãäºæãããã®ãšç°ãªãå Žåããšã©ãŒãªããžã§ã¯ãã«ãšã©ãŒãè¿œå ããŸãã 次ã«ã errãªããžã§ã¯ãããerrããã£ãŒã«ããšããŠretãªããžã§ã¯ãã«è¿œå ãããã®ãªããžã§ã¯ããè¿ããŸãã
ã¯ãšãªæ¹æ³ïŒ
Handle<Value> query(const Arguments& args) { HandleScope scope; Handle<Object> ret = Object::New(); Handle<Object> err = Object::New(); Handle<Array> rows = Array::New(); Handle <Script> script; Handle<Object> obj_row; node::Buffer *buf; MYSQL *mysql; MYSQL_RES *res; MYSQL_ROW row; MYSQL_FIELD *fields; unsigned int num_fields; bool ok=true; mysql = (MYSQL *)args.Holder()->GetHiddenValue( String::NewSymbol("MYSQL"))->ToObject()->GetIndexedPropertiesExternalArrayData(); if(!args.Holder()->GetHiddenValue(String::NewSymbol("connected"))->BooleanValue()){ err->Set(String::NewSymbol("id"), Uint32::New(65534)); err->Set(String::NewSymbol("text"), String::NewSymbol("You need to connect before any query")); ret->Set(String::NewSymbol("err"), err); ok = false; } if(ok == true){ if(args.Length()!=1){ ok=false; }else{ if(!args[0]->IsString()) ok=false; } if(ok == false){ err->Set(String::NewSymbol("id"), Uint32::New(65535)); err->Set(String::NewSymbol("text"), String::NewSymbol("Incorect parametrs of function")); } } if(ok == true){ String::AsciiValue query(args[0]->ToString()); if(mysql_query(mysql, *query)==0){ res = mysql_store_result(mysql); num_fields = mysql_num_fields(res); fields = mysql_fetch_fields(res); while ( (row = mysql_fetch_row(res)) ){ obj_row = Object::New(); for(unsigned int i=0; i<num_fields; i++){ switch(fields[i].type){ case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_INT24: case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: obj_row->Set(String::NewSymbol(fields[i].name), Number::New( (row[i])? atof(row[i]):0) ); break; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATE: case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_NEWDATE: script = Script::Compile( String::NewSymbol("")->Concat( String::NewSymbol("")->Concat( String::NewSymbol("new Date(Date.parse('"), String::NewSymbol( (row[i])? row[i]:"" ) ), String::NewSymbol("'))")) ); obj_row->Set(String::NewSymbol(fields[i].name), script->Run()); break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: if((fields[i].flags & BINARY_FLAG)){ buf = node::Buffer::New(row[i], mysql_fetch_lengths(res)[i]); obj_row->Set(String::NewSymbol(fields[i].name), buf->handle_); break; } default: obj_row->Set(String::NewSymbol(fields[i].name), String::NewSymbol( (row[i])? row[i]:"") ); break; } } rows->Set(rows->Length(),obj_row); } mysql_free_result(res); }; ret->Set(String::NewSymbol("inserted_id"), Uint32::New(mysql_insert_id(mysql))); ret->Set(String::NewSymbol("info"), String::NewSymbol( (mysql_info(mysql)) ? mysql_info(mysql) :"" )); ret->Set(String::NewSymbol("affected_rows"), Uint32::New(mysql_affected_rows(mysql))); err->Set(String::NewSymbol("id"), Uint32::New(mysql_errno(mysql))); err->Set(String::NewSymbol("text"), String::NewSymbol(mysql_error(mysql))); } ret->Set(String::NewSymbol("err"), err); ret->Set(String::NewSymbol("rows"), rows); return scope.Close(ret); }
ã¯ãšãªé¢æ°ãããæåã«éžæã®å®å šãªçµæãååŸããäžåºŠã«1è¡ãã€åŒãåºããããªãã®ã§ãçä¿¡ãã©ã¡ãŒã¿ãšæ¥ç¶ã確ç«ãããããã®ãã¹ãŠã®ãã§ãã¯ã®åŸã èŠæ±ãå®è¡ããå¿çãV8é åã®è¡ãªããžã§ã¯ãã«è¿œå ããŸãã åå¿çè¡ã¯ãããããã£åãã¯ãšãªçµæã®ãã£ãŒã«ãã®ååã§ãããªããžã§ã¯ãã«èšé²ãããå€ã¯åä¿¡ããå®éã®ããŒã¿ã§ãã æåã¯ãããè¡ãããã¹ãŠã®ããŒã¿ãV8 Stringã«å€æããŸãããããã䟿å©ãªçµæãå¿ èŠã§ããã
ãã®çµæãã¿ã€ããæã€ãã£ãŒã«ãã¯æ¬¡ã®ããã«æ±ºå®ãããŸããã
- MYSQL_TYPE_DECIMAL
- MYSQL_TYPE_TINY
- MYSQL_TYPE_SHORT
- MYSQL_TYPE_LONG
- MYSQL_TYPE_LONGLONG
- MYSQL_TYPE_INT24
- MYSQL_TYPE_FLOAT
- MYSQL_TYPE_DOUBLE
ã¿ã€ãã®ãããã£ãŒã«ãïŒ
- MYSQL_TYPE_TINY_BLOB
- MYSQL_TYPE_MEDIUM_BLOB
- MYSQL_TYPE_LONG_BLOB
- MYSQL_TYPE_BLOB
ãããŠãã¿ã€ããæã€ãã£ãŒã«ãïŒ
- MYSQL_TYPE_TIMESTAMP
- MYSQL_TYPE_DATE
- MYSQL_TYPE_TIME
- MYSQL_TYPE_DATETIME
- MYSQL_TYPE_YEAR
- MYSQL_TYPE_NEWDATE
ããããã¹ãŠã³ã³ãã€ã«ããå¿ èŠããããŸãããã®ããã«ã¯ã次ã®å 容ã®binding.gypãã¡ã€ã«ãäœæããå¿ èŠããããŸãã
{ "targets": [ { "target_name": "mysql_sync", "sources": [ "mysql_sync.cc" ], "include_dirs": [ '/server/daemons/mysql/include/mysql/' ], "link_settings": { 'libraries': ['-lmysqlclient -L/server/daemons/mysql/lib/mysql/'], 'library_dirs': ['/server/daemons/mysql/lib/mysql/'], }, } ] }
ããã«å¥åŠãªæ¹æ³ã瀺ãããŠãããšããäºå®ã«æ³šæãæãããã«ãé¡ãããŸãïŒããããããªãã«ãšã£ãŠã¯ç°ãªãã§ãããïŒã®ã§ãã³ãã³ãã䜿çšã§ããŸãïŒ
mysql_config --include --libs
ããã§ãå®è¡ãæ®ããŸãã
node-gyp configure
node-gyp build
cp build/Release/mysql_sync.node ./
ã¢ãžã¥ãŒã«ã¯ããã«äœ¿çšã§ããŸãããã¹ãã®ããã«ã次ã®ã³ãŒããäœæããŸãã
var mysql = require('./mysql_sync.node').create(); console.log(mysql.connect("localhost", "login", "pass", "test")); console.log(mysql.query("select * from tmp");
ãã®ãããªãŠãŒã¶ãŒãããŒã¿ããŒã¹ãããã³ããŒãã«ãããå Žåããã®ãããªãã®ãåŸãããŸãã
{ err: { id: 0, text: '' } } { inserted_id: 0, info: '', affected_rows: 1, err: { id: 0, text: '' }, rows:[ { number: 1558.235, varchar: 'test1', text: 'blob text2, blod: <SlowBuffer 31>, date: Wed Oct 03 2012 00:00:00 GMT+0400 (MSK), boolean: 1, tst: <SlowBuffer > } , { number: 2225, varchar: 'test2', text: 'blob text2, blod: <SlowBuffer 32>, date: Wed Oct 04 2012 00:00:00 GMT+0400 (MSK), boolean: 0, tst: <SlowBuffer > } ] }
次ã§äœæãããããŒãã«ã®çµæïŒ CREATE TABLE `tmp` ( `number` double NOT NULL default '0', `varchar` varchar(10) NOT NULL default '', `text` text NOT NULL, `blod` longblob NOT NULL, `date` datetime NOT NULL default '0000-00-00 00:00:00', `boolean` tinyint(1) NOT NULL default '0', `tst` longblob )
çµæïŒ
- ããã«äœ¿çšã§ããã¢ãžã¥ãŒã«ãçšæããŸãããããã¯æè¯ã§ã¯ãªããããããŸããããå¿ èŠãªããšãè¡ããŸããæã«ã¯ãããæãéèŠã§ã
- Node.jsã®C ++ã§ç¬èªã®ã¢ãžã¥ãŒã«ãäœæããæ¹æ³ãåŠã³ãŸãã
- ä»»æã®JavaScriptã³ãŒããåŒã³åºãã¢ãžã¥ãŒã«ããåŠã³ãŸãã
- å¿ èŠã«å¿ããŠä»ã®ã¢ãžã¥ãŒã«ãäœæã§ããŸã
- V8 JavaScriptãªããžã§ã¯ãã®çµéšãç©ãã
èšäºã¯äºæ³ãããé·ãããšãå€æããŸããããã¢ãžã¥ãŒã«ãäœæãããšãããŸãã¯ä»ã®éçºè ã®ã¢ãžã¥ãŒã«ã§äœãèµ·ãã£ãŠããã®ããç解ããããšãããšãã«å€ãã®äººã«ãšã£ãŠäŸ¿å©ã«ãªããšæããŸãã ãæž èŽããããšãããããŸããã