diff --git a/.vscode/launch.json b/.vscode/launch.json index a4c74ce..cbc7be1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "Launch via NPM", "request": "launch", "runtimeArgs": [ - "start" + "test" ], "runtimeExecutable": "npm", "skipFiles": [ diff --git a/README.md b/README.md index fd3411f..48f2ea1 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,11 @@ ## Pre-requirement You need to [install](https://crossdb.org/get-started/install/) crossdb lib first -> ### Not ready for production - ## Install ```sh npm install @croosdb/crossdb-nodejs ``` -### to Use +## Use ```javascript const CrossDB = require('@croosdb/crossdb-nodejs'); @@ -19,10 +17,129 @@ You need to [install](https://crossdb.org/get-started/install/) crossdb lib firs const db = new CrossDB('./<>/<>'); ``` +## Contributors -## Run Example + + + + + + + +
+ +
+ Efrem Ropelato +

+ 💻 Maintainer +
+ + + -```sh - npm install - npm start +## Example + +```javascript +// Import the CrossDB module +const CrossDB = require('@croosdb/crossdb-nodejs'); + +// Create an in-memory database instance +const db = new CrossDB(':memory:'); + +try { + // Create tables if they do not already exist + db.exec("CREATE TABLE IF NOT EXISTS student (id INT PRIMARY KEY, name CHAR(16), age INT, class CHAR(16), score FLOAT, info VARCHAR(255))"); + db.exec("CREATE TABLE IF NOT EXISTS teacher (id INT PRIMARY KEY, name CHAR(16), age INT, info CHAR(255), INDEX (name))"); + db.exec("CREATE TABLE IF NOT EXISTS book (id INT PRIMARY KEY, name CHAR(64), author CHAR(32), count INT, INDEX (name))"); + console.log("Tables 'student','teacher' and 'book' created."); + + // Clean (empty) all tables + db.exec("DELETE FROM student"); + db.exec("DELETE FROM teacher"); + db.exec("DELETE FROM book"); + + // Start a transaction + db.begin(); + console.log("Begin transaction"); + + // Insert sample data into the student, teacher, and book tables + db.exec("INSERT INTO student (id,name,age,class,score) VALUES (1,'jack',10,'3-1',90),(2,'tom',11,'2-5',91),(3,'jack',11,'1-6',92),(4,'rose',10,'4-2',90),(5,'tim',10,'3-1',95)"); + db.exec("INSERT INTO student (id,name,age,class,score,info) VALUES (6,'Tony',10,'3-1',95,'%s')", "He is a boy.\nHe likes playing football.\nWe all like him!"); + db.exec("INSERT INTO student (id,name,age,class,score,info) VALUES (7,'Wendy',10,'3-1',95,'%s')", "She is a girl.\nShe likes cooking.\nWe all love her!"); + db.exec("INSERT INTO teacher (id,name,age) VALUES (1,'Tomas',40),(2,'Steven',50),(3,'Bill',31),(4,'Lucy',29)"); + db.exec("INSERT INTO book (id,name,author,count) VALUES (1,'Romeo and Juliet','Shakespeare',10),(2,'Pride and Prejudice','Austen',5),(3,'Great Expectations','Dickens',8),(4,'Sorrows of Young Werther','Von Goethe',4)"); + console.log("Data inserted."); + + // Query to select all students + let res = db.exec("SELECT * FROM student"); + res.forEach((element,i) => { + console.log(i,"Select all students: ", element); + }); + + // Update a student's age and query the updated record + db.exec("UPDATE student set age=9 WHERE id = 2"); + console.log("Updated age to 9 for student with id = 2"); + + res = db.exec("SELECT id,name,age,class,score from student WHERE id = 2"); + res.forEach((element,i) => { + console.log(i,"Select student with id = 2: ", element); + }); + + // Delete a student and query the deleted record + db.exec("DELETE FROM student WHERE id = 3"); + console.log("Deleted student with id = 3"); + + res = db.exec("SELECT * from student WHERE id = 3"); + res.forEach((element,i) => { + console.log(i,"Select student with id = 3: ", element); + }); + + // Perform aggregation on the student table + res = db.exec("SELECT COUNT(*),MIN(score),MAX(score),SUM(score),AVG(score) FROM student"); + res.forEach((element,i) => { + console.log(i,"Student aggregation (COUNT, MIN, MAX, SUM, AVG): ", element); + }); + + // Commit the transaction + db.commit(); + console.log("Transaction committed"); + + // Start a new transaction + db.begin(); + console.log("Begin transaction"); + + // Update a student's age, query, and roll back the transaction + db.exec("UPDATE student set age=15 WHERE id = 2"); + console.log("Updated age to 15 for student with id = 2"); + + res = db.exec("SELECT id,name,age,class,score from student WHERE id = 2"); + res.forEach((element,i) => { + console.log(i,"Select student with id = 2: ", element); + }); + + db.rollback(); + console.log("Transaction rolled back"); + + res = db.exec("SELECT id,name,age,class,score from student WHERE id = 2"); + res.forEach((element,i) => { + console.log(i,"Select student with id = 2 after rollback: ", element); + }); + + // Execute multiple statements and query the results + res = db.exec("SELECT COUNT(*) as Count FROM student; SELECT id, name, age FROM student WHERE id=2;SELECT MIN(score) as min, MAX(score) as max, SUM(score) as sum, AVG(score) as avg FROM student"); + res.forEach((element,i) => { + console.log(i,"Multi-statement select: ", element); + }); + +} catch (err) { + // Handle errors, roll back any pending transaction, and log the error + console.error("Error during operation:", err); + db.rollback(); + console.log("Transaction rolled back due to error"); +} finally { + // Ensure the database connection is closed + console.log("\nCrossDB Simulation Complete."); + db.close(); + console.log("Database connection closed."); +} ``` diff --git a/example/index.js b/example/index.js index 5f781b0..3548e83 100644 --- a/example/index.js +++ b/example/index.js @@ -30,25 +30,33 @@ try { // Query to select all students let res = db.exec("SELECT * FROM student"); - console.log("Select all students: ", res); + res.forEach((element,i) => { + console.log(i,"Select all students: ", element); + }); // Update a student's age and query the updated record db.exec("UPDATE student set age=9 WHERE id = 2"); console.log("Updated age to 9 for student with id = 2"); res = db.exec("SELECT id,name,age,class,score from student WHERE id = 2"); - console.log("Select student with id = 2: ", res); + res.forEach((element,i) => { + console.log(i,"Select student with id = 2: ", element); + }); // Delete a student and query the deleted record db.exec("DELETE FROM student WHERE id = 3"); console.log("Deleted student with id = 3"); res = db.exec("SELECT * from student WHERE id = 3"); - console.log("Select student with id = 3: ", res); + res.forEach((element,i) => { + console.log(i,"Select student with id = 3: ", element); + }); // Perform aggregation on the student table res = db.exec("SELECT COUNT(*),MIN(score),MAX(score),SUM(score),AVG(score) FROM student"); - console.log("Student aggregation (COUNT, MIN, MAX, SUM, AVG): ", res); + res.forEach((element,i) => { + console.log(i,"Student aggregation (COUNT, MIN, MAX, SUM, AVG): ", element); + }); // Commit the transaction db.commit(); @@ -63,17 +71,23 @@ try { console.log("Updated age to 15 for student with id = 2"); res = db.exec("SELECT id,name,age,class,score from student WHERE id = 2"); - console.log("Select student with id = 2: ", res); + res.forEach((element,i) => { + console.log(i,"Select student with id = 2: ", element); + }); db.rollback(); console.log("Transaction rolled back"); res = db.exec("SELECT id,name,age,class,score from student WHERE id = 2"); - console.log("Select student with id = 2 after rollback: ", res); + res.forEach((element,i) => { + console.log(i,"Select student with id = 2 after rollback: ", element); + }); // Execute multiple statements and query the results - res = db.exec("SELECT COUNT(*) as Count FROM student; SELECT id,name FROM student WHERE id=2"); - console.log("Multi-statement select: ", res); + res = db.exec("SELECT COUNT(*) as Count FROM student; SELECT id, name, age FROM student WHERE id=2;SELECT MIN(score) as min, MAX(score) as max, SUM(score) as sum, AVG(score) as avg FROM student"); + res.forEach((element,i) => { + console.log(i,"Multi-statement select: ", element); + }); } catch (err) { // Handle errors, roll back any pending transaction, and log the error diff --git a/package.json b/package.json index 9847093..7a618b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@croosdb/crossdb-nodejs", - "version": "1.2.9", + "version": "1.3.0", "main": "index.js", "author": "Efrem Ropelato", "contributors":[ diff --git a/src/crossdb_binding.cc b/src/crossdb_binding.cc index 952a8d4..c7288ac 100644 --- a/src/crossdb_binding.cc +++ b/src/crossdb_binding.cc @@ -1,6 +1,24 @@ #include #include #include +#include +#include +#include +#include +#include + +std::vector str_split(const std::string &str, char delim) +{ + std::vector tokens; + std::stringstream ss(str); + std::string token; + + while (std::getline(ss, token, delim)) + { + tokens.push_back(token); + } + return tokens; +} class Connection : public Napi::ObjectWrap { @@ -42,69 +60,84 @@ class Connection : public Napi::ObjectWrap xdb_conn_t *conn; xdb_res_t *res; - Napi::Value Exec(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - - if (info.Length() < 1 || !info[0].IsString()) { - Napi::TypeError::New(env, "SQL query string expected").ThrowAsJavaScriptException(); - return env.Null(); - } + Napi::Value Exec(const Napi::CallbackInfo &info) + { + Napi::Env env = info.Env(); - std::string sql = info[0].As().Utf8Value(); - res = xdb_exec(conn, sql.c_str()); + if (info.Length() < 1 || !info[0].IsString()) + { + Napi::TypeError::New(env, "SQL query string expected").ThrowAsJavaScriptException(); + return env.Null(); + } - if (res == nullptr) { - Napi::Error::New(env, "Failed to execute SQL command").ThrowAsJavaScriptException(); - return env.Null(); - } + std::string sql = info[0].As().Utf8Value(); + Napi::Array results = Napi::Array::New(env); + int statementIndex = 0; + // Splitting example + auto tokens = str_split(sql, ';'); + for (const auto &token : tokens) + { + res = xdb_exec(conn, token.c_str()); + if (res == nullptr) + { + Napi::Error::New(env, "Failed to execute SQL command").ThrowAsJavaScriptException(); + return env.Null(); + } - if (res->errcode != 0) { - std::string errMsg = xdb_errmsg(res); - xdb_free_result(res); // Liberare la risorsa - Napi::Error::New(env, "SQL error " + std::to_string(res->errcode) + ": " + errMsg).ThrowAsJavaScriptException(); - return env.Null(); - } + if (res->errcode != 0) + { + std::string errMsg = xdb_errmsg(res); + xdb_free_result(res); // Liberare la risorsa + Napi::Error::New(env, "SQL error " + std::to_string(res->errcode) + ": " + errMsg).ThrowAsJavaScriptException(); + return env.Null(); + } - Napi::Array result = Napi::Array::New(env); - int rowIndex = 0; - - // Itera attraverso tutte le righe del risultato - while (xdb_row_t *row = xdb_fetch_row(res)) { - Napi::Object rowObj = Napi::Object::New(env); - - // Processa ogni colonna per la riga corrente - for (int i = 0; i < res->col_count; ++i) { - xdb_col_t *pCol = xdb_column_meta(res->col_meta, i); - Napi::Value value; - - switch (pCol->col_type) { - case XDB_TYPE_INT: - value = Napi::Number::New(env, xdb_column_int(static_cast(res->col_meta), row, static_cast(i))); - break; - case XDB_TYPE_BIGINT: - value = Napi::Number::New(env, xdb_column_int64(static_cast(res->col_meta), row, static_cast(i))); - break; - case XDB_TYPE_FLOAT: - value = Napi::Number::New(env, xdb_column_float(static_cast(res->col_meta), row, static_cast(i))); - break; - case XDB_TYPE_DOUBLE: - value = Napi::Number::New(env, xdb_column_double(static_cast(res->col_meta), row, static_cast(i))); - break; - case XDB_TYPE_CHAR: - value = Napi::String::New(env, xdb_column_str(static_cast(res->col_meta), row, static_cast(i))); - break; - default: - value = env.Null(); - break; + Napi::Array result = Napi::Array::New(env); + int rowIndex = 0; + + // Itera attraverso tutte le righe del risultato + while (xdb_row_t *row = xdb_fetch_row(res)) + { + Napi::Object rowObj = Napi::Object::New(env); + + // Processa ogni colonna per la riga corrente + for (int i = 0; i < res->col_count; ++i) + { + xdb_col_t *pCol = xdb_column_meta(res->col_meta, i); + Napi::Value value; + + switch (pCol->col_type) + { + case XDB_TYPE_INT: + value = Napi::Number::New(env, xdb_column_int(static_cast(res->col_meta), row, static_cast(i))); + break; + case XDB_TYPE_BIGINT: + value = Napi::Number::New(env, xdb_column_int64(static_cast(res->col_meta), row, static_cast(i))); + break; + case XDB_TYPE_FLOAT: + value = Napi::Number::New(env, xdb_column_float(static_cast(res->col_meta), row, static_cast(i))); + break; + case XDB_TYPE_DOUBLE: + value = Napi::Number::New(env, xdb_column_double(static_cast(res->col_meta), row, static_cast(i))); + break; + case XDB_TYPE_CHAR: + value = Napi::String::New(env, xdb_column_str(static_cast(res->col_meta), row, static_cast(i))); + break; + default: + value = env.Null(); + break; + } + rowObj.Set(Napi::String::New(env, pCol->col_name), value); + } + result.Set(rowIndex++, rowObj); } - rowObj.Set(Napi::String::New(env, pCol->col_name), value); + + xdb_free_result(res); // Rilascia il risultato una volta completato + results.Set(statementIndex++, result); } - result.Set(rowIndex++, rowObj); - } - xdb_free_result(res); // Rilascia il risultato una volta completato - return result; -} + return results; + } void Close(const Napi::CallbackInfo &info) {