GeezerVison is showing github.com — sqlite: return results with null prototype · nodejs/node@72c554a
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: src/node_sqlite.cc +45-43Lines changed: 45 additions & 43 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -25,6 +25,10 @@ using v8::FunctionTemplate; | 25 | 25 | using v8::Integer; | 26 | 26 | using v8::Isolate; | 27 | 27 | using v8::Local; | | 28 | +using v8::LocalVector; | | 29 | +using v8::MaybeLocal; | | 30 | +using v8::Name; | | 31 | +using v8::Null; | 28 | 32 | using v8::Number; | 29 | 33 | using v8::Object; | 30 | 34 | using v8::String; | @@ -405,7 +409,7 @@ bool StatementSync::BindValue(const Local<Value>& value, const int index) { | 405 | 409 | return true; | 406 | 410 | } | 407 | 411 |
| 408 | | -Local<Value> StatementSync::ColumnToValue(const int column) { | | 412 | +MaybeLocal<Value> StatementSync::ColumnToValue(const int column) { | 409 | 413 | switch (sqlite3_column_type(statement_, column)) { | 410 | 414 | case SQLITE_INTEGER: { | 411 | 415 | sqlite3_int64 value = sqlite3_column_int64(statement_, column); | @@ -419,7 +423,7 @@ Local<Value> StatementSync::ColumnToValue(const int column) { | 419 | 423 | "represented as a JavaScript number: %" PRId64, | 420 | 424 | column, | 421 | 425 | value); | 422 | | - return Local<Value>(); | | 426 | + return MaybeLocal<Value>(); | 423 | 427 | } | 424 | 428 | } | 425 | 429 | case SQLITE_FLOAT: | @@ -428,14 +432,10 @@ Local<Value> StatementSync::ColumnToValue(const int column) { | 428 | 432 | case SQLITE_TEXT: { | 429 | 433 | const char* value = reinterpret_cast<const char*>( | 430 | 434 | sqlite3_column_text(statement_, column)); | 431 | | - Local<Value> val; | 432 | | - if (!String::NewFromUtf8(env()->isolate(), value).ToLocal(&val)) { | 433 | | - return Local<Value>(); | 434 | | - } | 435 | | - return val; | | 435 | + return String::NewFromUtf8(env()->isolate(), value).As<Value>(); | 436 | 436 | } | 437 | 437 | case SQLITE_NULL: | 438 | | - return v8::Null(env()->isolate()); | | 438 | + return Null(env()->isolate()); | 439 | 439 | case SQLITE_BLOB: { | 440 | 440 | size_t size = | 441 | 441 | static_cast<size_t>(sqlite3_column_bytes(statement_, column)); | @@ -451,19 +451,15 @@ Local<Value> StatementSync::ColumnToValue(const int column) { | 451 | 451 | } | 452 | 452 | } | 453 | 453 |
| 454 | | -Local<Value> StatementSync::ColumnNameToValue(const int column) { | | 454 | +MaybeLocal<Name> StatementSync::ColumnNameToName(const int column) { | 455 | 455 | const char* col_name = sqlite3_column_name(statement_, column); | 456 | 456 | if (col_name == nullptr) { | 457 | 457 | node::THROW_ERR_INVALID_STATE( | 458 | 458 | env(), "Cannot get name of column %d", column); | 459 | | - return Local<Value>(); | | 459 | + return MaybeLocal<Name>(); | 460 | 460 | } | 461 | 461 |
| 462 | | - Local<String> key; | 463 | | - if (!String::NewFromUtf8(env()->isolate(), col_name).ToLocal(&key)) { | 464 | | - return Local<Value>(); | 465 | | - } | 466 | | - return key; | | 462 | + return String::NewFromUtf8(env()->isolate(), col_name).As<Name>(); | 467 | 463 | } | 468 | 464 |
| 469 | 465 | void StatementSync::MemoryInfo(MemoryTracker* tracker) const {} | @@ -474,38 +470,40 @@ void StatementSync::All(const FunctionCallbackInfo<Value>& args) { | 474 | 470 | Environment* env = Environment::GetCurrent(args); | 475 | 471 | THROW_AND_RETURN_ON_BAD_STATE( | 476 | 472 | env, stmt->IsFinalized(), "statement has been finalized"); | | 473 | + Isolate* isolate = env->isolate(); | 477 | 474 | int r = sqlite3_reset(stmt->statement_); | 478 | | - CHECK_ERROR_OR_THROW( | 479 | | - env->isolate(), stmt->db_->Connection(), r, SQLITE_OK, void()); | | 475 | + CHECK_ERROR_OR_THROW(isolate, stmt->db_->Connection(), r, SQLITE_OK, void()); | 480 | 476 |
| 481 | 477 | if (!stmt->BindParams(args)) { | 482 | 478 | return; | 483 | 479 | } | 484 | 480 |
| 485 | 481 | auto reset = OnScopeLeave([&]() { sqlite3_reset(stmt->statement_); }); | 486 | 482 | int num_cols = sqlite3_column_count(stmt->statement_); | 487 | | - std::vector<Local<Value>> rows; | | 483 | + LocalVector<Value> rows(isolate); | 488 | 484 | while ((r = sqlite3_step(stmt->statement_)) == SQLITE_ROW) { | 489 | | - Local<Object> row = Object::New(env->isolate()); | | 485 | + LocalVector<Name> row_keys(isolate); | | 486 | + row_keys.reserve(num_cols); | | 487 | + LocalVector<Value> row_values(isolate); | | 488 | + row_values.reserve(num_cols); | 490 | 489 |
| 491 | 490 | for (int i = 0; i < num_cols; ++i) { | 492 | | - Local<Value> key = stmt->ColumnNameToValue(i); | 493 | | - if (key.IsEmpty()) return; | 494 | | - Local<Value> val = stmt->ColumnToValue(i); | 495 | | - if (val.IsEmpty()) return; | 496 | | - | 497 | | - if (row->Set(env->context(), key, val).IsNothing()) { | 498 | | - return; | 499 | | - } | | 491 | + Local<Name> key; | | 492 | + if (!stmt->ColumnNameToName(i).ToLocal(&key)) return; | | 493 | + Local<Value> val; | | 494 | + if (!stmt->ColumnToValue(i).ToLocal(&val)) return; | | 495 | + row_keys.emplace_back(key); | | 496 | + row_values.emplace_back(val); | 500 | 497 | } | 501 | 498 |
| | 499 | + Local<Object> row = Object::New( | | 500 | + isolate, Null(isolate), row_keys.data(), row_values.data(), num_cols); | 502 | 501 | rows.emplace_back(row); | 503 | 502 | } | 504 | 503 |
| 505 | 504 | CHECK_ERROR_OR_THROW( | 506 | | - env->isolate(), stmt->db_->Connection(), r, SQLITE_DONE, void()); | 507 | | - args.GetReturnValue().Set( | 508 | | - Array::New(env->isolate(), rows.data(), rows.size())); | | 505 | + isolate, stmt->db_->Connection(), r, SQLITE_DONE, void()); | | 506 | + args.GetReturnValue().Set(Array::New(isolate, rows.data(), rows.size())); | 509 | 507 | } | 510 | 508 |
| 511 | 509 | void StatementSync::Get(const FunctionCallbackInfo<Value>& args) { | @@ -514,9 +512,9 @@ void StatementSync::Get(const FunctionCallbackInfo<Value>& args) { | 514 | 512 | Environment* env = Environment::GetCurrent(args); | 515 | 513 | THROW_AND_RETURN_ON_BAD_STATE( | 516 | 514 | env, stmt->IsFinalized(), "statement has been finalized"); | | 515 | + Isolate* isolate = env->isolate(); | 517 | 516 | int r = sqlite3_reset(stmt->statement_); | 518 | | - CHECK_ERROR_OR_THROW( | 519 | | - env->isolate(), stmt->db_->Connection(), r, SQLITE_OK, void()); | | 517 | + CHECK_ERROR_OR_THROW(isolate, stmt->db_->Connection(), r, SQLITE_OK, void()); | 520 | 518 |
| 521 | 519 | if (!stmt->BindParams(args)) { | 522 | 520 | return; | @@ -526,7 +524,7 @@ void StatementSync::Get(const FunctionCallbackInfo<Value>& args) { | 526 | 524 | r = sqlite3_step(stmt->statement_); | 527 | 525 | if (r == SQLITE_DONE) return; | 528 | 526 | if (r != SQLITE_ROW) { | 529 | | - THROW_ERR_SQLITE_ERROR(env->isolate(), stmt->db_->Connection()); | | 527 | + THROW_ERR_SQLITE_ERROR(isolate, stmt->db_->Connection()); | 530 | 528 | return; | 531 | 529 | } | 532 | 530 |
| @@ -535,19 +533,23 @@ void StatementSync::Get(const FunctionCallbackInfo<Value>& args) { | 535 | 533 | return; | 536 | 534 | } | 537 | 535 |
| 538 | | - Local<Object> result = Object::New(env->isolate()); | | 536 | + LocalVector<Name> keys(isolate); | | 537 | + keys.reserve(num_cols); | | 538 | + LocalVector<Value> values(isolate); | | 539 | + values.reserve(num_cols); | 539 | 540 |
| 540 | 541 | for (int i = 0; i < num_cols; ++i) { | 541 | | - Local<Value> key = stmt->ColumnNameToValue(i); | 542 | | - if (key.IsEmpty()) return; | 543 | | - Local<Value> val = stmt->ColumnToValue(i); | 544 | | - if (val.IsEmpty()) return; | 545 | | - | 546 | | - if (result->Set(env->context(), key, val).IsNothing()) { | 547 | | - return; | 548 | | - } | | 542 | + Local<Name> key; | | 543 | + if (!stmt->ColumnNameToName(i).ToLocal(&key)) return; | | 544 | + Local<Value> val; | | 545 | + if (!stmt->ColumnToValue(i).ToLocal(&val)) return; | | 546 | + keys.emplace_back(key); | | 547 | + values.emplace_back(val); | 549 | 548 | } | 550 | 549 |
| | 550 | + Local<Object> result = | | 551 | + Object::New(isolate, Null(isolate), keys.data(), values.data(), num_cols); | | 552 | + | 551 | 553 | args.GetReturnValue().Set(result); | 552 | 554 | } | 553 | 555 |
| @@ -676,7 +678,7 @@ Local<FunctionTemplate> StatementSync::GetConstructorTemplate( | 676 | 678 | if (tmpl.IsEmpty()) { | 677 | 679 | Isolate* isolate = env->isolate(); | 678 | 680 | tmpl = NewFunctionTemplate(isolate, IllegalConstructor); | 679 | | - tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "StatementSync")); | | 681 | + tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "StatementSync")); | 680 | 682 | tmpl->InstanceTemplate()->SetInternalFieldCount( | 681 | 683 | StatementSync::kInternalFieldCount); | 682 | 684 | SetProtoMethod(isolate, tmpl, "all", StatementSync::All); | |
Collapse file Copy file name to clipboardExpand all lines: src/node_sqlite.h +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -80,8 +80,8 @@ class StatementSync : public BaseObject { | 80 | 80 | std::optional<std::map<std::string, std::string>> bare_named_params_; | 81 | 81 | bool BindParams(const v8::FunctionCallbackInfo<v8::Value>& args); | 82 | 82 | bool BindValue(const v8::Local<v8::Value>& value, const int index); | 83 | | - v8::Local<v8::Value> ColumnToValue(const int column); | 84 | | - v8::Local<v8::Value> ColumnNameToValue(const int column); | | 83 | + v8::MaybeLocal<v8::Value> ColumnToValue(const int column); | | 84 | + v8::MaybeLocal<v8::Name> ColumnNameToName(const int column); | 85 | 85 | }; | 86 | 86 |
| 87 | 87 | } // namespace sqlite | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-sqlite-data-types.js +5-1Lines changed: 5 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -49,27 +49,31 @@ suite('data binding and mapping', () => { | 49 | 49 |
| 50 | 50 | const query = db.prepare('SELECT * FROM types WHERE key = ?'); | 51 | 51 | t.assert.deepStrictEqual(query.get(1), { | | 52 | + __proto__: null, | 52 | 53 | key: 1, | 53 | 54 | int: 42, | 54 | 55 | double: 3.14159, | 55 | 56 | text: 'foo', | 56 | 57 | buf: u8a, | 57 | 58 | }); | 58 | 59 | t.assert.deepStrictEqual(query.get(2), { | | 60 | + __proto__: null, | 59 | 61 | key: 2, | 60 | 62 | int: null, | 61 | 63 | double: null, | 62 | 64 | text: null, | 63 | 65 | buf: null, | 64 | 66 | }); | 65 | 67 | t.assert.deepStrictEqual(query.get(3), { | | 68 | + __proto__: null, | 66 | 69 | key: 3, | 67 | 70 | int: 8, | 68 | 71 | double: 2.718, | 69 | 72 | text: 'bar', | 70 | 73 | buf: new TextEncoder().encode('x☃y☃'), | 71 | 74 | }); | 72 | 75 | t.assert.deepStrictEqual(query.get(4), { | | 76 | + __proto__: null, | 73 | 77 | key: 4, | 74 | 78 | int: 99, | 75 | 79 | double: 0xf, | @@ -151,7 +155,7 @@ suite('data binding and mapping', () => { | 151 | 155 | ); | 152 | 156 | t.assert.deepStrictEqual( | 153 | 157 | db.prepare('SELECT * FROM data ORDER BY key').all(), | 154 | | - [{ key: 1, val: 5 }, { key: 2, val: null }], | | 158 | + [{ __proto__: null, key: 1, val: 5 }, { __proto__: null, key: 2, val: null }], | 155 | 159 | ); | 156 | 160 | }); | 157 | 161 | }); |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-sqlite-database-sync.js +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -143,8 +143,8 @@ suite('DatabaseSync.prototype.exec()', () => { | 143 | 143 | t.assert.strictEqual(result, undefined); | 144 | 144 | const stmt = db.prepare('SELECT * FROM data ORDER BY key'); | 145 | 145 | t.assert.deepStrictEqual(stmt.all(), [ | 146 | | - { key: 1, val: 2 }, | 147 | | - { key: 8, val: 9 }, | | 146 | + { __proto__: null, key: 1, val: 2 }, | | 147 | + { __proto__: null, key: 8, val: 9 }, | 148 | 148 | ]); | 149 | 149 | }); | 150 | 150 |
| |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-sqlite-named-parameters.js +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -42,7 +42,7 @@ suite('named parameters', () => { | 42 | 42 | stmt.run({ k: 1, v: 9 }); | 43 | 43 | t.assert.deepStrictEqual( | 44 | 44 | db.prepare('SELECT * FROM data').get(), | 45 | | - { key: 1, val: 9 }, | | 45 | + { __proto__: null, key: 1, val: 9 }, | 46 | 46 | ); | 47 | 47 | }); | 48 | 48 |
| @@ -57,7 +57,7 @@ suite('named parameters', () => { | 57 | 57 | stmt.run({ k: 1 }); | 58 | 58 | t.assert.deepStrictEqual( | 59 | 59 | db.prepare('SELECT * FROM data').get(), | 60 | | - { key: 1, val: 1 }, | | 60 | + { __proto__: null, key: 1, val: 1 }, | 61 | 61 | ); | 62 | 62 | }); | 63 | 63 |
| |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-sqlite-statement-sync.js +15-6Lines changed: 15 additions & 6 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -43,7 +43,15 @@ suite('StatementSync.prototype.get()', () => { | 43 | 43 | t.assert.strictEqual(stmt.get('key1', 'val1'), undefined); | 44 | 44 | t.assert.strictEqual(stmt.get('key2', 'val2'), undefined); | 45 | 45 | stmt = db.prepare('SELECT * FROM storage ORDER BY key'); | 46 | | - t.assert.deepStrictEqual(stmt.get(), { key: 'key1', val: 'val1' }); | | 46 | + t.assert.deepStrictEqual(stmt.get(), { __proto__: null, key: 'key1', val: 'val1' }); | | 47 | + }); | | 48 | + | | 49 | + test('executes a query that returns special columns', (t) => { | | 50 | + const db = new DatabaseSync(nextDb()); | | 51 | + t.after(() => { db.close(); }); | | 52 | + const stmt = db.prepare('SELECT 1 as __proto__, 2 as constructor, 3 as toString'); | | 53 | + // eslint-disable-next-line no-dupe-keys | | 54 | + t.assert.deepStrictEqual(stmt.get(), { __proto__: null, ['__proto__']: 1, constructor: 2, toString: 3 }); | 47 | 55 | }); | 48 | 56 | }); | 49 | 57 |
| @@ -71,8 +79,8 @@ suite('StatementSync.prototype.all()', () => { | 71 | 79 | ); | 72 | 80 | stmt = db.prepare('SELECT * FROM storage ORDER BY key'); | 73 | 81 | t.assert.deepStrictEqual(stmt.all(), [ | 74 | | - { key: 'key1', val: 'val1' }, | 75 | | - { key: 'key2', val: 'val2' }, | | 82 | + { __proto__: null, key: 'key1', val: 'val1' }, | | 83 | + { __proto__: null, key: 'key2', val: 'val2' }, | 76 | 84 | ]); | 77 | 85 | }); | 78 | 86 | }); | @@ -171,11 +179,11 @@ suite('StatementSync.prototype.setReadBigInts()', () => { | 171 | 179 | t.assert.strictEqual(setup, undefined); | 172 | 180 |
| 173 | 181 | const query = db.prepare('SELECT val FROM data'); | 174 | | - t.assert.deepStrictEqual(query.get(), { val: 42 }); | | 182 | + t.assert.deepStrictEqual(query.get(), { __proto__: null, val: 42 }); | 175 | 183 | t.assert.strictEqual(query.setReadBigInts(true), undefined); | 176 | | - t.assert.deepStrictEqual(query.get(), { val: 42n }); | | 184 | + t.assert.deepStrictEqual(query.get(), { __proto__: null, val: 42n }); | 177 | 185 | t.assert.strictEqual(query.setReadBigInts(false), undefined); | 178 | | - t.assert.deepStrictEqual(query.get(), { val: 42 }); | | 186 | + t.assert.deepStrictEqual(query.get(), { __proto__: null, val: 42 }); | 179 | 187 |
| 180 | 188 | const insert = db.prepare('INSERT INTO data (key) VALUES (?)'); | 181 | 189 | t.assert.deepStrictEqual( | @@ -223,6 +231,7 @@ suite('StatementSync.prototype.setReadBigInts()', () => { | 223 | 231 | const good = db.prepare(`SELECT ${Number.MAX_SAFE_INTEGER} + 1`); | 224 | 232 | good.setReadBigInts(true); | 225 | 233 | t.assert.deepStrictEqual(good.get(), { | | 234 | + __proto__: null, | 226 | 235 | [`${Number.MAX_SAFE_INTEGER} + 1`]: 2n ** 53n, | 227 | 236 | }); | 228 | 237 | }); | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-sqlite-transactions.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -37,7 +37,7 @@ suite('manual transactions', () => { | 37 | 37 | ); | 38 | 38 | t.assert.deepStrictEqual( | 39 | 39 | db.prepare('SELECT * FROM data').all(), | 40 | | - [{ key: 100 }], | | 40 | + [{ __proto__: null, key: 100 }], | 41 | 41 | ); | 42 | 42 | }); | 43 | 43 |
| |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-sqlite.js +4-4Lines changed: 4 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -77,11 +77,11 @@ test('in-memory databases are supported', (t) => { | 77 | 77 | t.assert.strictEqual(setup2, undefined); | 78 | 78 | t.assert.deepStrictEqual( | 79 | 79 | db1.prepare('SELECT * FROM data').all(), | 80 | | - [{ key: 1 }] | | 80 | + [{ __proto__: null, key: 1 }] | 81 | 81 | ); | 82 | 82 | t.assert.deepStrictEqual( | 83 | 83 | db2.prepare('SELECT * FROM data').all(), | 84 | | - [{ key: 1 }] | | 84 | + [{ __proto__: null, key: 1 }] | 85 | 85 | ); | 86 | 86 | }); | 87 | 87 |
| @@ -90,10 +90,10 @@ test('PRAGMAs are supported', (t) => { | 90 | 90 | t.after(() => { db.close(); }); | 91 | 91 | t.assert.deepStrictEqual( | 92 | 92 | db.prepare('PRAGMA journal_mode = WAL').get(), | 93 | | - { journal_mode: 'wal' }, | | 93 | + { __proto__: null, journal_mode: 'wal' }, | 94 | 94 | ); | 95 | 95 | t.assert.deepStrictEqual( | 96 | 96 | db.prepare('PRAGMA journal_mode').get(), | 97 | | - { journal_mode: 'wal' }, | | 97 | + { __proto__: null, journal_mode: 'wal' }, | 98 | 98 | ); | 99 | 99 | }); |
|