From 800f43a4189944b642a32aabf969fbd5a28c731b Mon Sep 17 00:00:00 2001 From: Alexander Makarenko Date: Fri, 1 Jun 2012 18:27:15 +0400 Subject: [PATCH 1/7] fixed .gitignore to ignore Intellij IDEA project files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 85c0975..b62253c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ devdependencies-stamp *.gcda *.gcno *.gcov +*.iml +.idea \ No newline at end of file From 45e35dfb166083ce52cf255dc97ef36b08037975 Mon Sep 17 00:00:00 2001 From: Alexander Makarenko Date: Fri, 1 Jun 2012 18:38:25 +0400 Subject: [PATCH 2/7] Added fetchAllSync() method --- src/mysql_bindings_statement.cc | 159 ++++++++++++++++++++++++++++++++ src/mysql_bindings_statement.h | 2 + 2 files changed, 161 insertions(+) diff --git a/src/mysql_bindings_statement.cc b/src/mysql_bindings_statement.cc index e58656d..a4a574d 100644 --- a/src/mysql_bindings_statement.cc +++ b/src/mysql_bindings_statement.cc @@ -52,6 +52,7 @@ void MysqlStatement::Init(Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "errnoSync", MysqlStatement::ErrnoSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "errorSync", MysqlStatement::ErrorSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "executeSync", MysqlStatement::ExecuteSync); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "fetchAllSync", MysqlStatement::FetchAllSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "fieldCountSync", MysqlStatement::FieldCountSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "freeResultSync", MysqlStatement::FreeResultSync); NODE_SET_PROTOTYPE_METHOD(constructor_template, "lastInsertIdSync", MysqlStatement::LastInsertIdSync); @@ -457,6 +458,164 @@ Handle MysqlStatement::ExecuteSync(const Arguments& args) { return scope.Close(True()); } +/** + * Returns row data from statement result + * + * @return {Object} + */ +Handle MysqlStatement::FetchAllSync(const Arguments& args) { + HandleScope scope; + + MysqlStatement *stmt = OBJUNWRAP(args.This()); + + MYSQLSTMT_MUSTBE_INITIALIZED; + MYSQLSTMT_MUSTBE_PREPARED; + + /* Get meta data for binding buffers */ + unsigned int field_count = mysql_stmt_field_count(stmt->_stmt); + uint32_t i = 0, j = 0; + unsigned long length[field_count]; + int row_count = 0; + my_bool is_null[field_count]; + MYSQL_BIND bind[field_count]; + MYSQL_RES *meta; + MYSQL_FIELD *fields; + + /* Buffers */ + int int_data[field_count]; + double double_data[field_count]; + char str_data[field_count][64]; + MYSQL_TIME date_data[field_count]; + memset(date_data, 0, sizeof(date_data)); + + memset(bind, 0, sizeof(bind)); + meta = mysql_stmt_result_metadata(stmt->_stmt); + fields = meta->fields; + while (i < field_count) { + bind[i].buffer_type = fields[i].type; + + switch(fields[i].type) { + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + bind[i].buffer = &int_data[i]; + break; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + bind[i].buffer = &double_data[i]; + break; + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_VARCHAR: + bind[i].buffer = (char *) str_data[i]; + bind[i].buffer_length = fields[i].length; + break; + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + bind[i].buffer = (char *) &date_data[i]; + } + + bind[i].is_null = &is_null[i]; + bind[i].length = &length[i]; + i++; + } + + /* If error on binding return null */ + if (mysql_stmt_bind_result(stmt->_stmt, bind)) { + return scope.Close(Null()); + } + + /* If error on buffering results return null */ + if (mysql_stmt_store_result(stmt->_stmt)) { + return scope.Close(Null()); + } + + Local js_result_rows = Array::New(); + Local js_result_row; + Local js_result; + + row_count = mysql_stmt_num_rows(stmt->_stmt); + + /* If no rows, return empty array */ + if (!row_count) { + return scope.Close(js_result_rows); + } + + i = 0; + while (mysql_stmt_fetch(stmt->_stmt) != MYSQL_NO_DATA) { + js_result_row = Object::New(); + + j = 0; + while (j < field_count) { + //fprintf(stdout, "Value: %s", buffers[j]); + switch(fields[j].type) { + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + //fprintf(stdout, "Value: %d (%ld)\n", int_data[j], length[j]); + js_result = Integer::New(int_data[j]); + break; + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + //js_result = Number::New(double_data[j]); + break; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + //fprintf(stdout, "Value: %f (%ld)\n", double_data[j], length[j]); + js_result = Number::New(double_data[j])->ToString(); + break; + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_VARCHAR: + //fprintf(stdout, "Value: %s (%ld)\n", str_data[j], length[j]); + js_result = V8STR2(str_data[j], length[j]); + break; + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + MYSQL_TIME ts = date_data[j]; + time_t rawtime; + struct tm * datetime; + time(&rawtime); + datetime = localtime(&rawtime); + datetime->tm_year = ts.year - 1900; + datetime->tm_mon = ts.month - 1; + datetime->tm_mday = ts.day; + datetime->tm_hour = ts.hour; + datetime->tm_min = ts.minute; + datetime->tm_sec = ts.second; + time_t timestamp = mktime(datetime); + + js_result = Date::New(1000 * (double) timestamp); + break; + } + + js_result_row->Set(V8STR(fields[j].name), js_result); + j++; + } + + js_result_rows->Set(Integer::NewFromUnsigned(i), js_result_row); + i++; + } + + return scope.Close(js_result_rows); +} + /** * Returns the number of field in the given statement * diff --git a/src/mysql_bindings_statement.h b/src/mysql_bindings_statement.h index 1df114b..62e4708 100644 --- a/src/mysql_bindings_statement.h +++ b/src/mysql_bindings_statement.h @@ -79,6 +79,8 @@ class MysqlStatement : public node::ObjectWrap { static Handle ExecuteSync(const Arguments& args); + static Handle FetchAllSync(const Arguments& args); + static Handle FieldCountSync(const Arguments& args); static Handle FreeResultSync(const Arguments& args); From 31ded51d50925cbcbc2855d5c9b255a619622d95 Mon Sep 17 00:00:00 2001 From: Alexander Makarenko Date: Fri, 1 Jun 2012 19:10:17 +0400 Subject: [PATCH 3/7] Disable temporarily warnings about not handled enums --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 23cc873..29c8586 100644 --- a/wscript +++ b/wscript @@ -23,7 +23,7 @@ def configure(conf): conf.check_tool("node_addon") # Enables all the warnings that are easy to avoid - conf.env.append_unique('CXXFLAGS', ["-Wall"]) + conf.env.append_unique('CXXFLAGS', ["-Wall", "-Wno-switch"]) if Options.options.warn: # Extra warnings conf.env.append_unique('CXXFLAGS', ["-Wextra"]) From 4154d3e0ccaa1743360f364203b4c5fc3be42da1 Mon Sep 17 00:00:00 2001 From: Alexander Makarenko Date: Fri, 1 Jun 2012 19:16:08 +0400 Subject: [PATCH 4/7] buffer for MYSQL_TYPE_TINY is unsigned int * --- src/mysql_bindings_statement.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mysql_bindings_statement.cc b/src/mysql_bindings_statement.cc index a4a574d..8355bcf 100644 --- a/src/mysql_bindings_statement.cc +++ b/src/mysql_bindings_statement.cc @@ -483,6 +483,7 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { /* Buffers */ int int_data[field_count]; + unsigned uint_data[field_count]; double double_data[field_count]; char str_data[field_count][64]; MYSQL_TIME date_data[field_count]; @@ -496,13 +497,15 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { switch(fields[i].type) { case MYSQL_TYPE_NULL: - case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_INT24: bind[i].buffer = &int_data[i]; break; + case MYSQL_TYPE_TINY: + bind[i].buffer = &uint_data[i]; + break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: case MYSQL_TYPE_DECIMAL: @@ -559,7 +562,6 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { //fprintf(stdout, "Value: %s", buffers[j]); switch(fields[j].type) { case MYSQL_TYPE_NULL: - case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: @@ -567,6 +569,9 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { //fprintf(stdout, "Value: %d (%ld)\n", int_data[j], length[j]); js_result = Integer::New(int_data[j]); break; + case MYSQL_TYPE_TINY: + js_result = Integer::NewFromUnsigned(uint_data[j]); + break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: //js_result = Number::New(double_data[j]); From 908f7853ca28573baeac8306eefe4aabf58abdef Mon Sep 17 00:00:00 2001 From: Alexander Makarenko Date: Fri, 1 Jun 2012 19:20:44 +0400 Subject: [PATCH 5/7] buffer for MYSQL_TYPE_TINY is unsigned int * --- src/mysql_bindings_statement.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mysql_bindings_statement.cc b/src/mysql_bindings_statement.cc index 8355bcf..212a998 100644 --- a/src/mysql_bindings_statement.cc +++ b/src/mysql_bindings_statement.cc @@ -483,7 +483,7 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { /* Buffers */ int int_data[field_count]; - unsigned uint_data[field_count]; + unsigned int uint_data[field_count]; double double_data[field_count]; char str_data[field_count][64]; MYSQL_TIME date_data[field_count]; From 8a1c3dae7e0e0bbd72cda468ec5b6a331edec499 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 1 Jun 2012 11:38:35 -0400 Subject: [PATCH 6/7] TINYINT is handled as must be handled --- src/mysql_bindings_statement.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mysql_bindings_statement.cc b/src/mysql_bindings_statement.cc index 212a998..01b9b71 100644 --- a/src/mysql_bindings_statement.cc +++ b/src/mysql_bindings_statement.cc @@ -483,7 +483,7 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { /* Buffers */ int int_data[field_count]; - unsigned int uint_data[field_count]; + signed char tiny_data[field_count]; double double_data[field_count]; char str_data[field_count][64]; MYSQL_TIME date_data[field_count]; @@ -504,7 +504,7 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { bind[i].buffer = &int_data[i]; break; case MYSQL_TYPE_TINY: - bind[i].buffer = &uint_data[i]; + bind[i].buffer = &tiny_data[i]; break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: @@ -570,7 +570,8 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { js_result = Integer::New(int_data[j]); break; case MYSQL_TYPE_TINY: - js_result = Integer::NewFromUnsigned(uint_data[j]); + fprintf(stdout, "Value: %u (%ld)\n", tiny_data[j], length[j]); + js_result = Integer::NewFromUnsigned(tiny_data[j]); break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: From 8207fca5e05d7150fa5f9eeb59607762917872cb Mon Sep 17 00:00:00 2001 From: Alexander Makarenko Date: Sun, 10 Jun 2012 19:30:25 -0400 Subject: [PATCH 7/7] Fixed conversion to and from boolean values: bool to TINYINT --- src/mysql_bindings_statement.cc | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/mysql_bindings_statement.cc b/src/mysql_bindings_statement.cc index 01b9b71..0067a4f 100644 --- a/src/mysql_bindings_statement.cc +++ b/src/mysql_bindings_statement.cc @@ -306,6 +306,15 @@ Handle MysqlStatement::BindParamsSync(const Arguments& args) { stmt->binds[i].buffer = int_data; stmt->binds[i].is_null = 0; stmt->binds[i].is_unsigned = false; + } else if (js_param->IsBoolean()) { + // I assume, booleans are usually stored as TINYINT(1) + int_data = new int; + *int_data = js_param->Int32Value(); + + stmt->binds[i].buffer_type = MYSQL_TYPE_TINY; + stmt->binds[i].buffer = int_data; + stmt->binds[i].is_null = 0; + stmt->binds[i].is_unsigned = false; } else if (js_param->IsUint32()) { uint_data = new unsigned int; *uint_data = js_param->Uint32Value(); @@ -472,7 +481,9 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { MYSQLSTMT_MUSTBE_PREPARED; /* Get meta data for binding buffers */ + unsigned int field_count = mysql_stmt_field_count(stmt->_stmt); + uint32_t i = 0, j = 0; unsigned long length[field_count]; int row_count = 0; @@ -490,7 +501,9 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { memset(date_data, 0, sizeof(date_data)); memset(bind, 0, sizeof(bind)); + meta = mysql_stmt_result_metadata(stmt->_stmt); + fields = meta->fields; while (i < field_count) { bind[i].buffer_type = fields[i].type; @@ -559,33 +572,32 @@ Handle MysqlStatement::FetchAllSync(const Arguments& args) { j = 0; while (j < field_count) { - //fprintf(stdout, "Value: %s", buffers[j]); switch(fields[j].type) { case MYSQL_TYPE_NULL: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_INT24: - //fprintf(stdout, "Value: %d (%ld)\n", int_data[j], length[j]); js_result = Integer::New(int_data[j]); break; case MYSQL_TYPE_TINY: - fprintf(stdout, "Value: %u (%ld)\n", tiny_data[j], length[j]); - js_result = Integer::NewFromUnsigned(tiny_data[j]); - break; + if (length[j] == 1) { + js_result = BooleanObject::New(tiny_data[j] == true); + } else { + js_result = Integer::NewFromUnsigned(tiny_data[j]); + } + break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: - //js_result = Number::New(double_data[j]); + js_result = Number::New(double_data[j]); break; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: - //fprintf(stdout, "Value: %f (%ld)\n", double_data[j], length[j]); js_result = Number::New(double_data[j])->ToString(); break; case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: - //fprintf(stdout, "Value: %s (%ld)\n", str_data[j], length[j]); js_result = V8STR2(str_data[j], length[j]); break; case MYSQL_TYPE_YEAR: