Skip to content

Commit

Permalink
add exec multiple statements
Browse files Browse the repository at this point in the history
  • Loading branch information
efremropelato committed Nov 24, 2024
1 parent 32f86d5 commit 00f6e18
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"name": "Launch via NPM",
"request": "launch",
"runtimeArgs": [
"start"
"test"
],
"runtimeExecutable": "npm",
"skipFiles": [
Expand Down
131 changes: 124 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -19,10 +17,129 @@ You need to [install](https://crossdb.org/get-started/install/) crossdb lib firs
const db = new CrossDB('./<<path>>/<<db name>>');
```

## Contributors

## Run Example
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center">
<a href="https://github.com/efremropelato">
<img src="https://avatars.githubusercontent.com/u/6368884?v=4?s=100" width="100px;" alt=""/><br />
<sub><b>Efrem Ropelato</b></sub>
</a><br />
<a href="https://github.com/crossdb-org/crossdb-nodejs/commits?author=efremropelato" title="Code">💻 <i>Maintainer</i></a>
</td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->

```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.");
}
```
30 changes: 22 additions & 8 deletions example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@croosdb/crossdb-nodejs",
"version": "1.2.9",
"version": "1.3.0",
"main": "index.js",
"author": "Efrem Ropelato",
"contributors":[
Expand Down
145 changes: 89 additions & 56 deletions src/crossdb_binding.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
#include <napi.h>
#include <crossdb.h>
#include <string>
#include <vector>
#include <sstream>
#include <cassert>
#include <cstring>
#include <memory>

std::vector<std::string> str_split(const std::string &str, char delim)
{
std::vector<std::string> 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<Connection>
{
Expand Down Expand Up @@ -42,69 +60,84 @@ class Connection : public Napi::ObjectWrap<Connection>
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<Napi::String>().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<Napi::String>().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<uint64_t>(res->col_meta), row, static_cast<uint16_t>(i)));
break;
case XDB_TYPE_BIGINT:
value = Napi::Number::New(env, xdb_column_int64(static_cast<uint64_t>(res->col_meta), row, static_cast<uint16_t>(i)));
break;
case XDB_TYPE_FLOAT:
value = Napi::Number::New(env, xdb_column_float(static_cast<uint64_t>(res->col_meta), row, static_cast<uint16_t>(i)));
break;
case XDB_TYPE_DOUBLE:
value = Napi::Number::New(env, xdb_column_double(static_cast<uint64_t>(res->col_meta), row, static_cast<uint16_t>(i)));
break;
case XDB_TYPE_CHAR:
value = Napi::String::New(env, xdb_column_str(static_cast<uint64_t>(res->col_meta), row, static_cast<uint16_t>(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<uint64_t>(res->col_meta), row, static_cast<uint16_t>(i)));
break;
case XDB_TYPE_BIGINT:
value = Napi::Number::New(env, xdb_column_int64(static_cast<uint64_t>(res->col_meta), row, static_cast<uint16_t>(i)));
break;
case XDB_TYPE_FLOAT:
value = Napi::Number::New(env, xdb_column_float(static_cast<uint64_t>(res->col_meta), row, static_cast<uint16_t>(i)));
break;
case XDB_TYPE_DOUBLE:
value = Napi::Number::New(env, xdb_column_double(static_cast<uint64_t>(res->col_meta), row, static_cast<uint16_t>(i)));
break;
case XDB_TYPE_CHAR:
value = Napi::String::New(env, xdb_column_str(static_cast<uint64_t>(res->col_meta), row, static_cast<uint16_t>(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)
{
Expand Down

0 comments on commit 00f6e18

Please sign in to comment.