Skip to content

Commit 928e4a2

Browse files
committed
sqlite: refactor prepared statement iterator
This commit refactors the StatementSync iterator implementation in two primary ways: - The iterator internal state is no longer exposed to JavaScript. - The iterator prevents the prepared statement from being GC'ed. Fixes: #57493
1 parent 5a7b7d2 commit 928e4a2

File tree

4 files changed

+198
-185
lines changed

4 files changed

+198
-185
lines changed

src/env_properties.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@
448448
V(shutdown_wrap_template, v8::ObjectTemplate) \
449449
V(socketaddress_constructor_template, v8::FunctionTemplate) \
450450
V(sqlite_statement_sync_constructor_template, v8::FunctionTemplate) \
451+
V(sqlite_statement_sync_iterator_constructor_template, v8::FunctionTemplate) \
451452
V(sqlite_session_constructor_template, v8::FunctionTemplate) \
452453
V(streambaseentry_ctor_template, v8::FunctionTemplate) \
453454
V(streambaseoutputstream_constructor_template, v8::ObjectTemplate) \

src/node_sqlite.cc

Lines changed: 122 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ using v8::ConstructorBehavior;
2525
using v8::Context;
2626
using v8::DontDelete;
2727
using v8::Exception;
28-
using v8::External;
2928
using v8::Function;
3029
using v8::FunctionCallback;
3130
using v8::FunctionCallbackInfo;
@@ -1615,142 +1614,12 @@ void StatementSync::All(const FunctionCallbackInfo<Value>& args) {
16151614
args.GetReturnValue().Set(Array::New(isolate, rows.data(), rows.size()));
16161615
}
16171616

1618-
void StatementSync::IterateReturnCallback(
1619-
const FunctionCallbackInfo<Value>& args) {
1620-
Environment* env = Environment::GetCurrent(args);
1621-
auto isolate = env->isolate();
1622-
auto context = isolate->GetCurrentContext();
1623-
1624-
auto self = args.This();
1625-
// iterator has fetch all result or break, prevent next func to return result
1626-
if (self->Set(context, env->isfinished_string(), Boolean::New(isolate, true))
1627-
.IsNothing()) {
1628-
// An error will have been scheduled.
1629-
return;
1630-
}
1631-
1632-
Local<Value> val;
1633-
if (!self->Get(context, env->statement_string()).ToLocal(&val)) {
1634-
// An error will have been scheduled.
1635-
return;
1636-
}
1637-
auto external_stmt = Local<External>::Cast(val);
1638-
auto stmt = static_cast<StatementSync*>(external_stmt->Value());
1639-
if (!stmt->IsFinalized()) {
1640-
sqlite3_reset(stmt->statement_);
1641-
}
1642-
1643-
LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()});
1644-
LocalVector<Value> values(isolate,
1645-
{Boolean::New(isolate, true), Null(isolate)});
1646-
1647-
DCHECK_EQ(keys.size(), values.size());
1648-
Local<Object> result = Object::New(
1649-
isolate, Null(isolate), keys.data(), values.data(), keys.size());
1650-
args.GetReturnValue().Set(result);
1651-
}
1652-
1653-
void StatementSync::IterateNextCallback(
1654-
const FunctionCallbackInfo<Value>& args) {
1655-
Environment* env = Environment::GetCurrent(args);
1656-
auto isolate = env->isolate();
1657-
auto context = isolate->GetCurrentContext();
1658-
1659-
auto self = args.This();
1660-
1661-
Local<Value> val;
1662-
if (!self->Get(context, env->isfinished_string()).ToLocal(&val)) {
1663-
// An error will have been scheduled.
1664-
return;
1665-
}
1666-
1667-
// skip iteration if is_finished
1668-
auto is_finished = Local<Boolean>::Cast(val);
1669-
if (is_finished->Value()) {
1670-
Local<Name> keys[] = {env->done_string(), env->value_string()};
1671-
Local<Value> values[] = {Boolean::New(isolate, true), Null(isolate)};
1672-
static_assert(arraysize(keys) == arraysize(values));
1673-
Local<Object> result = Object::New(
1674-
isolate, Null(isolate), &keys[0], &values[0], arraysize(keys));
1675-
args.GetReturnValue().Set(result);
1676-
return;
1677-
}
1678-
1679-
if (!self->Get(context, env->statement_string()).ToLocal(&val)) {
1680-
// An error will have been scheduled.
1681-
return;
1682-
}
1683-
1684-
auto external_stmt = Local<External>::Cast(val);
1685-
auto stmt = static_cast<StatementSync*>(external_stmt->Value());
1686-
1687-
if (!self->Get(context, env->num_cols_string()).ToLocal(&val)) {
1688-
// An error will have been scheduled.
1689-
return;
1690-
}
1691-
1692-
auto num_cols = Local<Integer>::Cast(val)->Value();
1693-
1694-
THROW_AND_RETURN_ON_BAD_STATE(
1695-
env, stmt->IsFinalized(), "statement has been finalized");
1696-
1697-
int r = sqlite3_step(stmt->statement_);
1698-
if (r != SQLITE_ROW) {
1699-
CHECK_ERROR_OR_THROW(
1700-
env->isolate(), stmt->db_.get(), r, SQLITE_DONE, void());
1701-
1702-
// cleanup when no more rows to fetch
1703-
sqlite3_reset(stmt->statement_);
1704-
if (self->Set(
1705-
context, env->isfinished_string(), Boolean::New(isolate, true))
1706-
.IsNothing()) {
1707-
// An error would have been scheduled
1708-
return;
1709-
}
1710-
1711-
LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()});
1712-
LocalVector<Value> values(isolate,
1713-
{Boolean::New(isolate, true), Null(isolate)});
1714-
1715-
DCHECK_EQ(keys.size(), values.size());
1716-
Local<Object> result = Object::New(
1717-
isolate, Null(isolate), keys.data(), values.data(), keys.size());
1718-
args.GetReturnValue().Set(result);
1719-
return;
1720-
}
1721-
1722-
LocalVector<Name> row_keys(isolate);
1723-
row_keys.reserve(num_cols);
1724-
LocalVector<Value> row_values(isolate);
1725-
row_values.reserve(num_cols);
1726-
for (int i = 0; i < num_cols; ++i) {
1727-
Local<Name> key;
1728-
if (!stmt->ColumnNameToName(i).ToLocal(&key)) return;
1729-
Local<Value> val;
1730-
if (!stmt->ColumnToValue(i).ToLocal(&val)) return;
1731-
row_keys.emplace_back(key);
1732-
row_values.emplace_back(val);
1733-
}
1734-
1735-
Local<Object> row = Object::New(
1736-
isolate, Null(isolate), row_keys.data(), row_values.data(), num_cols);
1737-
1738-
LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()});
1739-
LocalVector<Value> values(isolate, {Boolean::New(isolate, false), row});
1740-
1741-
DCHECK_EQ(keys.size(), values.size());
1742-
Local<Object> result = Object::New(
1743-
isolate, Null(isolate), keys.data(), values.data(), keys.size());
1744-
args.GetReturnValue().Set(result);
1745-
}
1746-
17471617
void StatementSync::Iterate(const FunctionCallbackInfo<Value>& args) {
17481618
StatementSync* stmt;
17491619
ASSIGN_OR_RETURN_UNWRAP(&stmt, args.This());
17501620
Environment* env = Environment::GetCurrent(args);
17511621
THROW_AND_RETURN_ON_BAD_STATE(
17521622
env, stmt->IsFinalized(), "statement has been finalized");
1753-
auto isolate = env->isolate();
17541623
auto context = env->context();
17551624
int r = sqlite3_reset(stmt->statement_);
17561625
CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void());
@@ -1759,67 +1628,28 @@ void StatementSync::Iterate(const FunctionCallbackInfo<Value>& args) {
17591628
return;
17601629
}
17611630

1762-
Local<Function> next_func;
1763-
Local<Function> return_func;
1764-
if (!Function::New(context, StatementSync::IterateNextCallback)
1765-
.ToLocal(&next_func) ||
1766-
!Function::New(context, StatementSync::IterateReturnCallback)
1767-
.ToLocal(&return_func)) {
1768-
// An error will have been scheduled.
1769-
return;
1770-
}
1771-
1772-
Local<Name> keys[] = {env->next_string(), env->return_string()};
1773-
Local<Value> values[] = {next_func, return_func};
1774-
static_assert(arraysize(keys) == arraysize(values));
1775-
17761631
Local<Object> global = context->Global();
17771632
Local<Value> js_iterator;
17781633
Local<Value> js_iterator_prototype;
1779-
if (!global->Get(context, env->iterator_string()).ToLocal(&js_iterator))
1634+
if (!global->Get(context, env->iterator_string()).ToLocal(&js_iterator)) {
17801635
return;
1636+
}
17811637
if (!js_iterator.As<Object>()
17821638
->Get(context, env->prototype_string())
1783-
.ToLocal(&js_iterator_prototype))
1784-
return;
1785-
1786-
Local<Object> iterable_iterator = Object::New(
1787-
isolate, js_iterator_prototype, &keys[0], &values[0], arraysize(keys));
1788-
1789-
auto num_cols_pd = v8::PropertyDescriptor(
1790-
v8::Integer::New(isolate, sqlite3_column_count(stmt->statement_)), false);
1791-
num_cols_pd.set_enumerable(false);
1792-
num_cols_pd.set_configurable(false);
1793-
if (iterable_iterator
1794-
->DefineProperty(context, env->num_cols_string(), num_cols_pd)
1795-
.IsNothing()) {
1796-
// An error will have been scheduled.
1639+
.ToLocal(&js_iterator_prototype)) {
17971640
return;
17981641
}
17991642

1800-
auto stmt_pd =
1801-
v8::PropertyDescriptor(v8::External::New(isolate, stmt), false);
1802-
stmt_pd.set_enumerable(false);
1803-
stmt_pd.set_configurable(false);
1804-
if (iterable_iterator
1805-
->DefineProperty(context, env->statement_string(), stmt_pd)
1643+
BaseObjectPtr<StatementSyncIterator> iter =
1644+
StatementSyncIterator::Create(env, BaseObjectPtr<StatementSync>(stmt));
1645+
if (iter->object()
1646+
->GetPrototype()
1647+
.As<Object>()
1648+
->SetPrototype(context, js_iterator_prototype)
18061649
.IsNothing()) {
1807-
// An error will have been scheduled.
18081650
return;
18091651
}
1810-
1811-
auto is_finished_pd =
1812-
v8::PropertyDescriptor(v8::Boolean::New(isolate, false), true);
1813-
stmt_pd.set_enumerable(false);
1814-
stmt_pd.set_configurable(false);
1815-
if (iterable_iterator
1816-
->DefineProperty(context, env->isfinished_string(), is_finished_pd)
1817-
.IsNothing()) {
1818-
// An error will have been scheduled.
1819-
return;
1820-
}
1821-
1822-
args.GetReturnValue().Set(iterable_iterator);
1652+
args.GetReturnValue().Set(iter->object());
18231653
}
18241654

18251655
void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {
@@ -2118,6 +1948,118 @@ BaseObjectPtr<StatementSync> StatementSync::Create(
21181948
return MakeBaseObject<StatementSync>(env, obj, std::move(db), stmt);
21191949
}
21201950

1951+
StatementSyncIterator::StatementSyncIterator(Environment* env,
1952+
Local<Object> object,
1953+
BaseObjectPtr<StatementSync> stmt)
1954+
: BaseObject(env, object), stmt_(std::move(stmt)) {
1955+
MakeWeak();
1956+
done_ = false;
1957+
}
1958+
1959+
StatementSyncIterator::~StatementSyncIterator() {}
1960+
void StatementSyncIterator::MemoryInfo(MemoryTracker* tracker) const {}
1961+
1962+
Local<FunctionTemplate> StatementSyncIterator::GetConstructorTemplate(
1963+
Environment* env) {
1964+
Local<FunctionTemplate> tmpl =
1965+
env->sqlite_statement_sync_iterator_constructor_template();
1966+
if (tmpl.IsEmpty()) {
1967+
Isolate* isolate = env->isolate();
1968+
tmpl = NewFunctionTemplate(isolate, IllegalConstructor);
1969+
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "StatementSyncIterator"));
1970+
tmpl->InstanceTemplate()->SetInternalFieldCount(
1971+
StatementSync::kInternalFieldCount);
1972+
SetProtoMethod(isolate, tmpl, "next", StatementSyncIterator::Next);
1973+
SetProtoMethod(isolate, tmpl, "return", StatementSyncIterator::Return);
1974+
env->set_sqlite_statement_sync_iterator_constructor_template(tmpl);
1975+
}
1976+
return tmpl;
1977+
}
1978+
1979+
BaseObjectPtr<StatementSyncIterator> StatementSyncIterator::Create(
1980+
Environment* env, BaseObjectPtr<StatementSync> stmt) {
1981+
Local<Object> obj;
1982+
if (!GetConstructorTemplate(env)
1983+
->InstanceTemplate()
1984+
->NewInstance(env->context())
1985+
.ToLocal(&obj)) {
1986+
return BaseObjectPtr<StatementSyncIterator>();
1987+
}
1988+
1989+
return MakeBaseObject<StatementSyncIterator>(env, obj, std::move(stmt));
1990+
}
1991+
1992+
void StatementSyncIterator::Next(const FunctionCallbackInfo<Value>& args) {
1993+
StatementSyncIterator* iter;
1994+
ASSIGN_OR_RETURN_UNWRAP(&iter, args.This());
1995+
Environment* env = Environment::GetCurrent(args);
1996+
THROW_AND_RETURN_ON_BAD_STATE(
1997+
env, iter->stmt_->IsFinalized(), "statement has been finalized");
1998+
Isolate* isolate = env->isolate();
1999+
LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()});
2000+
2001+
if (iter->done_) {
2002+
LocalVector<Value> values(isolate,
2003+
{Boolean::New(isolate, true), Null(isolate)});
2004+
Local<Object> result = Object::New(
2005+
isolate, Null(isolate), keys.data(), values.data(), keys.size());
2006+
args.GetReturnValue().Set(result);
2007+
return;
2008+
}
2009+
2010+
int r = sqlite3_step(iter->stmt_->statement_);
2011+
if (r != SQLITE_ROW) {
2012+
CHECK_ERROR_OR_THROW(
2013+
env->isolate(), iter->stmt_->db_.get(), r, SQLITE_DONE, void());
2014+
sqlite3_reset(iter->stmt_->statement_);
2015+
LocalVector<Value> values(isolate,
2016+
{Boolean::New(isolate, true), Null(isolate)});
2017+
Local<Object> result = Object::New(
2018+
isolate, Null(isolate), keys.data(), values.data(), keys.size());
2019+
args.GetReturnValue().Set(result);
2020+
return;
2021+
}
2022+
2023+
int num_cols = sqlite3_column_count(iter->stmt_->statement_);
2024+
LocalVector<Name> row_keys(isolate);
2025+
LocalVector<Value> row_values(isolate);
2026+
row_keys.reserve(num_cols);
2027+
row_values.reserve(num_cols);
2028+
for (int i = 0; i < num_cols; ++i) {
2029+
Local<Name> key;
2030+
if (!iter->stmt_->ColumnNameToName(i).ToLocal(&key)) return;
2031+
Local<Value> val;
2032+
if (!iter->stmt_->ColumnToValue(i).ToLocal(&val)) return;
2033+
row_keys.emplace_back(key);
2034+
row_values.emplace_back(val);
2035+
}
2036+
2037+
Local<Object> row = Object::New(
2038+
isolate, Null(isolate), row_keys.data(), row_values.data(), num_cols);
2039+
LocalVector<Value> values(isolate, {Boolean::New(isolate, false), row});
2040+
Local<Object> result = Object::New(
2041+
isolate, Null(isolate), keys.data(), values.data(), keys.size());
2042+
args.GetReturnValue().Set(result);
2043+
}
2044+
2045+
void StatementSyncIterator::Return(const FunctionCallbackInfo<Value>& args) {
2046+
StatementSyncIterator* iter;
2047+
ASSIGN_OR_RETURN_UNWRAP(&iter, args.This());
2048+
Environment* env = Environment::GetCurrent(args);
2049+
THROW_AND_RETURN_ON_BAD_STATE(
2050+
env, iter->stmt_->IsFinalized(), "statement has been finalized");
2051+
Isolate* isolate = env->isolate();
2052+
2053+
sqlite3_reset(iter->stmt_->statement_);
2054+
iter->done_ = true;
2055+
LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()});
2056+
LocalVector<Value> values(isolate,
2057+
{Boolean::New(isolate, true), Null(isolate)});
2058+
Local<Object> result = Object::New(
2059+
isolate, Null(isolate), keys.data(), values.data(), keys.size());
2060+
args.GetReturnValue().Set(result);
2061+
}
2062+
21212063
Session::Session(Environment* env,
21222064
Local<Object> object,
21232065
BaseObjectWeakPtr<DatabaseSync> database,

src/node_sqlite.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,29 @@ class StatementSync : public BaseObject {
142142
v8::MaybeLocal<v8::Value> ColumnToValue(const int column);
143143
v8::MaybeLocal<v8::Name> ColumnNameToName(const int column);
144144

145-
static void IterateNextCallback(
146-
const v8::FunctionCallbackInfo<v8::Value>& args);
147-
static void IterateReturnCallback(
148-
const v8::FunctionCallbackInfo<v8::Value>& args);
145+
friend class StatementSyncIterator;
146+
};
147+
148+
class StatementSyncIterator : public BaseObject {
149+
public:
150+
StatementSyncIterator(Environment* env,
151+
v8::Local<v8::Object> object,
152+
BaseObjectPtr<StatementSync> stmt);
153+
void MemoryInfo(MemoryTracker* tracker) const override;
154+
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
155+
Environment* env);
156+
static BaseObjectPtr<StatementSyncIterator> Create(
157+
Environment* env, BaseObjectPtr<StatementSync> stmt);
158+
static void Next(const v8::FunctionCallbackInfo<v8::Value>& args);
159+
static void Return(const v8::FunctionCallbackInfo<v8::Value>& args);
160+
161+
SET_MEMORY_INFO_NAME(StatementSyncIterator)
162+
SET_SELF_SIZE(StatementSyncIterator)
163+
164+
private:
165+
~StatementSyncIterator() override;
166+
BaseObjectPtr<StatementSync> stmt_;
167+
bool done_;
149168
};
150169

151170
using Sqlite3ChangesetGenFunc = int (*)(sqlite3_session*, int*, void**);

0 commit comments

Comments
 (0)