Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expanded Sql for Statement #1679

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/sqlite3.d.ts
Expand Up @@ -76,7 +76,7 @@ export class Statement extends events.EventEmitter {

finalize(callback?: (err: Error) => void): Database;

run(callback?: (err: Error | null) => void): this;
run(callback?: (this: RunResult, err: Error | null) => void): this;
run(params: any, callback?: (this: RunResult, err: Error | null) => void): this;
run(...params: any[]): this;

Expand All @@ -91,6 +91,8 @@ export class Statement extends events.EventEmitter {
each(callback?: (err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this;
each(params: any, callback?: (this: RunResult, err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this;
each(...params: any[]): this;

readonly expandedSql: string;
}

export class Database extends events.EventEmitter {
Expand Down
9 changes: 8 additions & 1 deletion src/macros.h
Expand Up @@ -165,6 +165,13 @@ inline bool OtherIsInt(Napi::Number source) {
} \
sqlite3_mutex* name = sqlite3_db_mutex(stmt->db->_handle);

#define STATEMENT_EXPAND_SQL(stmt) \
if (stmt->_handle != NULL) { \
Napi::Env env = stmt->Env(); \
Napi::Value expanded_sql = Napi::String::New(env, sqlite3_expanded_sql(stmt->_handle)); \
(stmt->Value()).Set(Napi::String::New(env, "expandedSql"), expanded_sql); \
}

#define STATEMENT_END() \
assert(stmt->locked); \
assert(stmt->db->pending); \
Expand Down Expand Up @@ -210,6 +217,6 @@ inline bool OtherIsInt(Napi::Number source) {
case SQLITE_BLOB: delete (Values::Blob*)(field); break; \
case SQLITE_NULL: delete (Values::Null*)(field); break; \
} \
}
}

#endif
20 changes: 17 additions & 3 deletions src/statement.cc
Expand Up @@ -161,6 +161,8 @@ void Statement::Work_AfterPrepare(napi_env e, napi_status status, void* data) {
Napi::Env env = stmt->Env();
Napi::HandleScope scope(env);

STATEMENT_EXPAND_SQL(stmt);

if (stmt->status != SQLITE_OK) {
Error(baton.get());
stmt->Finalize_();
Expand All @@ -170,6 +172,7 @@ void Statement::Work_AfterPrepare(napi_env e, napi_status status, void* data) {
if (!baton->callback.IsEmpty() && baton->callback.Value().IsFunction()) {
Napi::Function cb = baton->callback.Value();
Napi::Value argv[] = { env.Null() };

TRY_CATCH_CALL(stmt->Value(), cb, 1, argv);
}
}
Expand Down Expand Up @@ -359,6 +362,7 @@ void Statement::Work_Bind(napi_env e, void* data) {
STATEMENT_MUTEX(mtx);
sqlite3_mutex_enter(mtx);
stmt->Bind(baton->parameters);

sqlite3_mutex_leave(mtx);
}

Expand All @@ -369,6 +373,8 @@ void Statement::Work_AfterBind(napi_env e, napi_status status, void* data) {
Napi::Env env = stmt->Env();
Napi::HandleScope scope(env);

STATEMENT_EXPAND_SQL(stmt);

if (stmt->status != SQLITE_OK) {
Error(baton.get());
}
Expand Down Expand Up @@ -436,6 +442,8 @@ void Statement::Work_AfterGet(napi_env e, napi_status status, void* data) {
Napi::Env env = stmt->Env();
Napi::HandleScope scope(env);

STATEMENT_EXPAND_SQL(stmt);

if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) {
Error(baton.get());
}
Expand Down Expand Up @@ -510,6 +518,8 @@ void Statement::Work_AfterRun(napi_env e, napi_status status, void* data) {
Napi::Env env = stmt->Env();
Napi::HandleScope scope(env);

STATEMENT_EXPAND_SQL(stmt);

if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) {
Error(baton.get());
}
Expand All @@ -518,8 +528,7 @@ void Statement::Work_AfterRun(napi_env e, napi_status status, void* data) {
Napi::Function cb = baton->callback.Value();
if (IS_FUNCTION(cb)) {
(stmt->Value()).Set(Napi::String::New(env, "lastID"), Napi::Number::New(env, baton->inserted_id));
(stmt->Value()).Set( Napi::String::New(env, "changes"), Napi::Number::New(env, baton->changes));

(stmt->Value()).Set(Napi::String::New(env, "changes"), Napi::Number::New(env, baton->changes));
Napi::Value argv[] = { env.Null() };
TRY_CATCH_CALL(stmt->Value(), cb, 1, argv);
}
Expand Down Expand Up @@ -580,6 +589,8 @@ void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) {
Napi::Env env = stmt->Env();
Napi::HandleScope scope(env);

STATEMENT_EXPAND_SQL(stmt);

if (stmt->status != SQLITE_DONE) {
Error(baton.get());
}
Expand Down Expand Up @@ -704,6 +715,8 @@ void Statement::AsyncEach(uv_async_t* handle) {
Napi::Env env = async->stmt->Env();
Napi::HandleScope scope(env);

STATEMENT_EXPAND_SQL(async->stmt);

while (true) {
// Get the contents out of the data cache for us to process in the JS callback.
Rows rows;
Expand Down Expand Up @@ -752,10 +765,11 @@ void Statement::Work_AfterEach(napi_env e, napi_status status, void* data) {
Napi::Env env = stmt->Env();
Napi::HandleScope scope(env);

STATEMENT_EXPAND_SQL(stmt);

if (stmt->status != SQLITE_DONE) {
Error(baton.get());
}

STATEMENT_END();
}

Expand Down
4 changes: 2 additions & 2 deletions src/statement.h
Expand Up @@ -208,7 +208,7 @@ class Statement : public Napi::ObjectWrap<Statement> {
WORK_DEFINITION(Reset);

Napi::Value Finalize_(const Napi::CallbackInfo& info);

protected:
static void Work_BeginPrepare(Database::Baton* baton);
static void Work_Prepare(napi_env env, void* data);
Expand All @@ -223,7 +223,7 @@ class Statement : public Napi::ObjectWrap<Statement> {
template <class T> inline Values::Field* BindParameter(const Napi::Value source, T pos);
template <class T> T* Bind(const Napi::CallbackInfo& info, int start = 0, int end = -1);
bool Bind(const Parameters &parameters);

static void GetRow(Row* row, sqlite3_stmt* stmt);
static Napi::Value RowToJS(Napi::Env env, Row* row);
void Schedule(Work_Callback callback, Baton* baton);
Expand Down
142 changes: 142 additions & 0 deletions test/expandedSql.test.js
@@ -0,0 +1,142 @@
var sqlite3 = require('../lib/sqlite3');
var assert = require('assert');

describe('expandedSql', function() {
var db;
before(function(done) {
db = new sqlite3.Database(':memory:');
db.run("CREATE TABLE foo (id INT PRIMARY KEY, count INT)", done);
});
after(function(done) {
db.wait(done);
});

it('should expand the sql with bindings within callbacks for Statement', function() {
db.serialize(() => {
/** test Statement methods */
let stmt = db.prepare("INSERT INTO foo VALUES(?,?)", function() {
assert.equal(this.expandedSql, "INSERT INTO foo VALUES(NULL,NULL)");
return this;
});
/** bind */
stmt.bind(3,4, function() {
assert.equal(this.expandedSql, "INSERT INTO foo VALUES(3,4)");
});
stmt.bind(1,2, function() {
assert.equal(this.expandedSql, "INSERT INTO foo VALUES(1,2)");
});
/** reset */
stmt.reset(function() {
// currently it is being retained
assert.equal(this.expandedSql, "INSERT INTO foo VALUES(1,2)");
});

// only a utility for callbacks
assert.equal(stmt.expandedSql, undefined);

stmt = db.prepare("INSERT INTO foo VALUES(?,?)", function() {
assert.equal(this.expandedSql, "INSERT INTO foo VALUES(NULL,NULL)");
return this;
});
/** run */
stmt.run([2,2], function() {
assert.equal(this.expandedSql, "INSERT INTO foo VALUES(2,2)");
});
stmt.run(4,5, function() {
assert.equal(this.expandedSql, "INSERT INTO foo VALUES(4,5)");
});
stmt.finalize();

/** get */
stmt = db.prepare("select * from foo where id = $id;");
stmt.get(function() {
assert.equal(this.expandedSql, "select * from foo where id = NULL;");
});
stmt.get({ $id: 1 }, function() {
assert.equal(this.expandedSql, "select * from foo where id = 1;");
});
stmt.get([2], function() {
assert.equal(this.expandedSql, "select * from foo where id = 2;");
});

/** all */
stmt.all(1, function() {
assert.equal(this.expandedSql, "select * from foo where id = 1;");
});
stmt.all({ $id: 2 }, function() {
assert.equal(this.expandedSql, "select * from foo where id = 2;");
});
stmt.all([3], function() {
assert.equal(this.expandedSql, "select * from foo where id = 3;");
});

/** each - testing within each callback */
stmt.each(1, function() {
assert.equal(this.expandedSql, "select * from foo where id = 1;");
}, function() {
assert.equal(this.expandedSql, "select * from foo where id = 1;");
});
stmt.each({ $id: 2 }, function() {
assert.equal(this.expandedSql, "select * from foo where id = 2;");
}, function() {
assert.equal(this.expandedSql, "select * from foo where id = 2;");
});
stmt.each([3], function() {
assert.equal(this.expandedSql, "select * from foo where id = 3;");
}, function() {
assert.equal(this.expandedSql, "select * from foo where id = 3;");
});

stmt.finalize();
});
});

it('should expand the sql with bindings within callbacks for Database', function() {
db.serialize(() => {
db.prepare("INSERT INTO foo VALUES(?,?)", function() {
assert.equal(this.expandedSql, "INSERT INTO foo VALUES(NULL,NULL)");
});

/** run */
db.run("INSERT INTO foo VALUES(?,?)", 10, 11, function () {
assert.equal(this.expandedSql, "INSERT INTO foo VALUES(10,11)");
});

/** get */
db.get("select * from foo;", function() {
assert.equal(this.expandedSql, "select * from foo;");
});
db.get("select * from bar;", function() {
// since this is a sqlite error there is nothing to expand afterwards
assert.equal(this.expandedSql, undefined);
});

/** all */
db.all("SELECT * FROM foo;", function(err, rows) {
assert.equal(this.expandedSql, "SELECT * FROM foo;");
assert.deepStrictEqual(rows.sort((a,b) => a.id - b.id), [{ id: 2, count: 2 }, { id: 4, count: 5}, { id: 10, count: 11 }]);
});
db.all("SELECT id, count FROM foo WHERE id = ?", 1, function(err, rows) {
assert.equal(this.expandedSql, "SELECT id, count FROM foo WHERE id = 1");
assert.deepStrictEqual(rows, []);
});

/** each */
db.each("select * from foo;", function() {
assert.equal(this.expandedSql, "select * from foo;");
}, function() {
assert.equal(this.expandedSql, "select * from foo;");
});
db.each("select * from bar;", function() {
// since this is a sqlite error there is nothing to expand afterwards
assert.equal(this.expandedSql, undefined);
});

/** exec */
db.exec("select * from foo where id = ?", function() {
// not applicable here so it is never set
assert.equal(this.expandedSql, undefined);
});
});
});
});