fixed bug in edge case

This commit is contained in:
Joshua Wise
2021-05-12 23:46:33 -05:00
parent 27f6f1779a
commit 189f5ba42c
4 changed files with 77 additions and 19 deletions
+14 -5
View File
@@ -1734,6 +1734,7 @@ int CustomTable::xBestIndex (sqlite3_vtab * vtab, sqlite3_index_info * output)
{
int parameter_count = VTab::Upcast(vtab)->parameter_count;
int argument_count = 0;
std::vector<std::pair<int, int>> forwarded;
for (int i = 0, len = output->nConstraint; i < len; ++i) {
auto item = output->aConstraint[i];
@@ -1753,10 +1754,18 @@ int CustomTable::xBestIndex (sqlite3_vtab * vtab, sqlite3_index_info * output)
return SQLITE_CONSTRAINT;
}
forwarded.emplace_back(item.iColumn, i);
}
}
output->aConstraintUsage[i].argvIndex = ++argument_count;
output->aConstraintUsage[i].omit = 1;
output->idxNum |= 1 << item.iColumn;
std::sort(forwarded.begin(), forwarded.end());
for (std::pair<int, int> pair : forwarded) {
int bit = 1 << pair.first;
if (!(output->idxNum & bit)) {
output->idxNum |= bit;
output->aConstraintUsage[pair.second].argvIndex = ++argument_count;
output->aConstraintUsage[pair.second].omit = 1;
}
}
@@ -1765,9 +1774,9 @@ int CustomTable::xBestIndex (sqlite3_vtab * vtab, sqlite3_index_info * output)
output->estimatedCost = output->estimatedRows = 1000000000 / (argument_count + 1);
return SQLITE_OK;
}
#line 378 "./src/util/custom-table.lzz"
#line 387 "./src/util/custom-table.lzz"
void CustomTable::PropagateJSError ()
#line 378 "./src/util/custom-table.lzz"
#line 387 "./src/util/custom-table.lzz"
{
assert(db->GetState()->was_js_error == false);
db->GetState()->was_js_error = true;
+10 -10
View File
@@ -663,17 +663,17 @@ private:
static int xRowid (sqlite3_vtab_cursor * cursor, sqlite_int64 * output);
#line 343 "./src/util/custom-table.lzz"
static int xBestIndex (sqlite3_vtab * vtab, sqlite3_index_info * output);
#line 378 "./src/util/custom-table.lzz"
void PropagateJSError ();
#line 383 "./src/util/custom-table.lzz"
Addon * const addon;
#line 384 "./src/util/custom-table.lzz"
v8::Isolate * const isolate;
#line 385 "./src/util/custom-table.lzz"
Database * const db;
#line 386 "./src/util/custom-table.lzz"
std::string const name;
#line 387 "./src/util/custom-table.lzz"
void PropagateJSError ();
#line 392 "./src/util/custom-table.lzz"
Addon * const addon;
#line 393 "./src/util/custom-table.lzz"
v8::Isolate * const isolate;
#line 394 "./src/util/custom-table.lzz"
Database * const db;
#line 395 "./src/util/custom-table.lzz"
std::string const name;
#line 396 "./src/util/custom-table.lzz"
CopyablePersistent <v8::Function> const factory;
};
#line 64 "./src/util/data.lzz"
+13 -4
View File
@@ -343,6 +343,7 @@ private:
static int xBestIndex(sqlite3_vtab* vtab, sqlite3_index_info* output) {
int parameter_count = VTab::Upcast(vtab)->parameter_count;
int argument_count = 0;
std::vector<std::pair<int, int>> forwarded;
for (int i = 0, len = output->nConstraint; i < len; ++i) {
auto item = output->aConstraint[i];
@@ -362,10 +363,18 @@ private:
// could appear undefined in the generator function.
return SQLITE_CONSTRAINT;
}
// Tell SQLite to forward arguments to xFilter.
output->aConstraintUsage[i].argvIndex = ++argument_count;
output->aConstraintUsage[i].omit = 1;
output->idxNum |= 1 << item.iColumn;
forwarded.emplace_back(item.iColumn, i);
}
}
// Tell SQLite to forward arguments to xFilter.
std::sort(forwarded.begin(), forwarded.end());
for (std::pair<int, int> pair : forwarded) {
int bit = 1 << pair.first;
if (!(output->idxNum & bit)) {
output->idxNum |= bit;
output->aConstraintUsage[pair.second].argvIndex = ++argument_count;
output->aConstraintUsage[pair.second].omit = 1;
}
}
+40
View File
@@ -166,16 +166,56 @@ describe('Database#table()', function () {
.to.throw(Database.SqliteError);
});
it('should correctly handle arguments even when used out of order', function () {
const calls = [];
this.db.table('vtab', {
columns: ['x', 'y'],
*rows(x, y) {
calls.push([...arguments]);
yield { x, y };
},
});
expect(this.db.prepare('SELECT * FROM vtab WHERE "$1" = ? AND "$2" = ?').get(10, 5))
.to.deep.equal({ x: 10, y: 5 });
expect(calls.splice(0)).to.deep.equal([[10, 5]]);
expect(this.db.prepare('SELECT * FROM vtab WHERE "$2" = ? AND "$1" = ?').get(5, 10))
.to.deep.equal({ x: 10, y: 5 });
expect(calls.splice(0)).to.deep.equal([[10, 5]]);
expect(this.db.prepare('SELECT * FROM vtab WHERE "$2" = ? AND "$2" = ? AND "$1" = ?').get(5, 5, 10))
.to.deep.equal({ x: 10, y: 5 });
expect(calls.splice(0)).to.deep.equal([[10, 5]]);
expect(this.db.prepare('SELECT * FROM vtab WHERE "$2" = ? AND "$2" = ? AND "$1" = ?').get(5, 9, 10))
.to.be.undefined;
expect(calls.splice(0)).to.deep.equal([[10, 5]]);
expect(this.db.prepare('SELECT * FROM vtab WHERE "$2" = ? AND "$2" = ? AND "$1" = ?').get(9, 5, 10))
.to.be.undefined;
expect(calls.splice(0)).to.deep.equal([[10, 9]]);
});
it.skip('should correctly handle arguments that are constrained to other arguments', function () {
// This test case fails due to a bug in SQLite (as of May 2021):
// https://sqlite.org/forum/forumpost?udc=1&name=830d37b928&t=c
const calls = [];
this.db.table('vtab', {
columns: ['x', 'y'],
*rows(x, y) {
calls.push([...arguments]);
yield { x, y };
},
});
expect(this.db.prepare('SELECT * FROM vtab WHERE "$1" = ? AND "$2" = "$1"').get(10))
.to.deep.equal({ x: 10, y: 10 });
expect(calls.splice(0)).to.deep.equal([[10, 10]]);
expect(this.db.prepare('SELECT * FROM vtab WHERE "$2" = "$1" AND "$1" = ?').get(10))
.to.deep.equal({ x: 10, y: 10 });
expect(calls.splice(0)).to.deep.equal([[10, 10]]);
expect(this.db.prepare('SELECT * FROM vtab WHERE "$2" = ? AND "$2" = "$1" AND "$1" = ?').get(10, 10))
.to.deep.equal({ x: 10, y: 10 });
expect(calls.splice(0)).to.deep.equal([[10, 10]]);
expect(this.db.prepare('SELECT * FROM vtab WHERE "$2" = ? AND "$2" = "$1" AND "$1" = ?').get(5, 10))
.to.be.undefined;
expect(calls.splice(0)).to.deep.equal([[10, 5]]);
expect(this.db.prepare('SELECT * FROM vtab WHERE "$2" = "$1" AND "$2" = ? AND "$1" = ?').get(5, 10))
.to.be.undefined;
expect(calls.splice(0)).to.deep.equal([[10, 10]]);
});
it('should throw an exception if the database is busy', function () {
let ranOnce = false;