1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Get the JS SQLTester command handlers in place sans those which have to run SQL.

FossilOrigin-Name: d21b1217964a53f33b7ba3958b34aa8560dff8ede33e66f54aa0afbab7099ec3
This commit is contained in:
stephan
2023-08-29 15:39:57 +00:00
parent 0fc20a32c0
commit 69a55ca17d
6 changed files with 424 additions and 56 deletions

View File

@@ -64,11 +64,9 @@ SQLTesterException.toss = (...args)=>{
}
class DbException extends SQLTesterException {
constructor(...args){
super(...args);
//TODO...
//const db = args[0];
//if( db instanceof sqlite3.oo1.DB )
constructor(pDb, rc, closeDb){
super("DB error #"+rc+": "+sqlite3.capi.sqlite3_errmsg(pDb));
if( closeDb ) sqlite3.capi.sqlite3_close_v2(pDb);
}
isFatal() { return true; }
}
@@ -150,9 +148,14 @@ class Outer {
return this;
}
setOutputPrefix( func ){
this.getOutputPrefix = func;
return this;
outputPrefix(){
if( 0==arguments.length ){
return (this.getOutputPrefix
? (this.getOutputPrefix() ?? '') : '');
}else{
this.getOutputPrefix = arguments[0];
return this;
}
}
verboseN(lvl, argv){
@@ -179,7 +182,7 @@ class Outer {
class SQLTester {
#outer = new Outer().setOutputPrefix( ()=>'SQLTester: ' );
#outer = new Outer().outputPrefix( ()=>'SQLTester: ' );
#aFiles = [];
#inputBuffer = [];
#resultBuffer = [];
@@ -189,16 +192,34 @@ class SQLTester {
});
#emitColNames = false;
#keepGoing = false;
#aDb = [];
#db = newObj({
list: [],
iCurrent: 0,
list: new Array(7),
iCurrentDb: 0,
initialDbName: "test.db",
initSql: ['select 1;'],
currentDb: function(){
return this.list[this.iCurrentDb];
}
});
constructor(){
}
outln(...args){ return this.#outer.outln(...args); }
out(...args){ return this.#outer.out(...args); }
reset(){
this.clearInputBuffer();
this.clearResultBuffer();
this.#clearBuffer(this.#db.initSql);
this.closeAllDbs();
this.nTest = 0;
this.nullView = "nil";
this.emitColNames = false;
this.#db.iCurrentDb = 0;
this.#db.initSql.push("SELECT 1;");
}
appendInput(line, addNL){
this.#inputBuffer.push(line);
if( addNL ) this.#inputBuffer.push('\n');
@@ -208,20 +229,195 @@ class SQLTester {
if( addNL ) this.#resultBuffer.push('\n');
}
clearInputBuffer(){
this.#inputBuffer.length = 0;
return this.#inputBuffer;
}
clearResultBuffer(){
this.#resultBuffer.length = 0;
return this.#resultBuffer;
#clearBuffer(buffer){
buffer.length = 0;
return buffer;
}
clearInputBuffer(){ return this.#clearBuffer(this.#inputBuffer); }
clearResultBuffer(){return this.#clearBuffer(this.#resultBuffer); }
getInputText(){ return this.#inputBuffer.join(''); }
getResultText(){ return this.#resultBuffer.join(''); }
#takeBuffer(buffer){
const s = buffer.join('');
buffer.length = 0;
return s;
}
takeInputBuffer(){
return this.#takeBuffer(this.#inputBuffer);
}
takeResultBuffer(){
return this.#takeBuffer(this.#resultBuffer);
}
verbosity(...args){ return this.#outer.verbosity(...args); }
nullValue(){
if( 0==arguments.length ){
return this.#nullView;
}else{
this.#nullView = ''+arguments[0];
}
}
outputColumnNames(){
if( 0==arguments.length ){
return this.#emitColNames;
}else{
this.#emitColNames = !!arguments[0];
}
}
currentDbId(){
if( 0==arguments.length ){
return this.#db.iCurrentDb;
}else{
this.#affirmDbId(arguments[0]).#db.iCurrentDb = arguments[0];
}
}
#affirmDbId(id){
if(id<0 || id>=this.#db.list.length){
toss(SQLTesterException, "Database index ",id," is out of range.");
}
return this;
}
currentDb(...args){
if( 0!=args.length ){
this.#affirmDbId(id).#db.iCurrentDb = id;
}
return this.#db.currentDb();
}
getDbById(id){
return this.#affirmDbId(id).#db.list[id];
}
getCurrentDb(){ return this.#db.list[this.#db.iCurrentDb]; }
closeDb(id) {
if( 0==arguments.length ){
id = this.#db.iCurrentDb;
}
const db = this.#affirmDbId(id).#db.list[id];
if( db ){
sqlite3.capi.sqlite3_close_v2(db);
this.#db.list[id] = null;
}
}
closeAllDbs(){
for(let i = 0; i<this.#db.list.length; ++i){
if(this.#db.list[i]){
sqlite3.capi.sqlite3_close_v2(this.#db.list[i]);
this.#db.list[i] = null;
}
}
this.#db.iCurrentDb = 0;
}
openDb(name, createIfNeeded){
if( 3===arguments.length ){
const slot = arguments[0];
this.#affirmDbId(slot).#db.iCurrentDb = slot;
name = arguments[1];
createIfNeeded = arguments[2];
}
this.closeDb();
const capi = sqlite3.capi, wasm = sqlite3.wasm;
let pDb = 0;
let flags = capi.SQLITE_OPEN_READWRITE;
if( createIfNeeded ) flags |= capi.SQLITE_OPEN_CREATE;
try{
let rc;
wasm.pstack.call(function(){
let ppOut = wasm.pstack.allocPtr();
rc = sqlite3.capi.sqlite3_open_v2(name, ppOut, flags, null);
pDb = wasm.peekPtr(ppOut);
});
if( 0==rc && this.#db.initSql.length > 0){
//this.#outer.verbose2("RUNNING DB INIT CODE: ",this.#db.initSql.toString());
rc = this.execSql(pDb, false, ResultBufferMode.NONE,
null, this.#db.initSql.join(''));
}
if( 0!=rc ){
sqlite3.SQLite3Error.toss(
rc,
"sqlite3 result code",rc+":",
(pDb ? sqlite3.capi.sqlite3_errmsg(pDb)
: sqlite3.capi.sqlite3_errstr(rc))
);
}
return this.#db.list[this.#db.iCurrentDb] = pDb;
}catch(e){
sqlite3.capi.sqlite3_close_v2(pDb);
throw e;
}
}
#setupInitialDb(){
if( !this.#db.list[0] ){
Util.unlink(this.#db.initialDbName);
this.openDb(0, this.#db.initialDbName, true);
}else{
this.#outer.outln("WARNING: setupInitialDb() unexpectedly ",
"triggered while it is opened.");
}
}
/**
Returns v or some escaped form of v, as defined in the tester's
spec doc.
*/
#escapeSqlValue(v){
if( !v ) return "{}";
if( !Rx.special.test(v) ){
return v /* no escaping needed */;
}
if( !Rx.squiggly.test(v) ){
return "{"+v+"}";
}
const sb = ["\""];
const n = v.length;
for(let i = 0; i < n; ++i){
const ch = v.charAt(i);
switch(ch){
case '\\': sb.push("\\\\"); break;
case '"': sb.push("\\\""); break;
default:{
//verbose("CHAR ",(int)ch," ",ch," octal=",String.format("\\%03o", (int)ch));
const ccode = ch.charCodeAt(i);
if( ccode < 32 ) sb.push('\\',ccode.toString(8),'o');
else sb.push(ch);
break;
}
}
}
sb.append("\"");
return sb.join('');
}
#appendDbErr(pDb, sb, rc){
sb.push(sqlite3.capi.sqlite3_js_rc_str(rc), ' ');
const msg = this.#escapeSqlValue(sqlite3.capi.sqlite3_errmsg(pDb));
if( '{' == msg.charAt(0) ){
sb.push(msg);
}else{
sb.push('{', msg, '}');
}
}
execSql(pDb, throwOnError, appendMode, lineMode, sql){
sql = sqlite3.capi.sqlite3_js_sql_to_string(sql);
this.#outer.outln("execSql() is TODO. ",sql);
return 0;
}
}/*SQLTester*/
class Command {
@@ -246,16 +442,6 @@ class Command {
}
}
class TestCase extends Command {
process(tester, script, argv){
this.argcCheck(script, argv,1);
script.testCaseName(argv[1]);
tester.clearResultBuffer();
tester.clearInputBuffer();
}
}
class Cursor {
src;
sb = [];
@@ -287,7 +473,11 @@ const Rx = newObj({
requiredProperties: / REQUIRED_PROPERTIES:[ \t]*(\S.*)\s*$/,
scriptModuleName: / SCRIPT_MODULE_NAME:[ \t]*(\S+)\s*$/,
mixedModuleName: / ((MIXED_)?MODULE_NAME):[ \t]*(\S+)\s*$/,
command: /^--(([a-z-]+)( .*)?)$/
command: /^--(([a-z-]+)( .*)?)$/,
//! "Special" characters - we have to escape output if it contains any.
special: /[\x00-\x20\x22\x5c\x7b\x7d]/,
//! Either of '{' or '}'.
squiggly: /[{}]/
});
class TestScript {
@@ -295,7 +485,7 @@ class TestScript {
#moduleName = null;
#filename = null;
#testCaseName = null;
#outer = new Outer().setOutputPrefix( ()=>this.getOutputPrefix() );
#outer = new Outer().outputPrefix( ()=>this.getOutputPrefix()+': ' );
constructor(...args){
let content, filename;
@@ -307,7 +497,6 @@ class TestScript {
}
this.#filename = filename;
this.#cursor.src = content;
this.#outer.outputPrefix = ()=>this.getOutputPrefix();
}
testCaseName(){
@@ -318,7 +507,7 @@ class TestScript {
getOutputPrefix() {
let rc = "["+(this.#moduleName || this.#filename)+"]";
if( this.#testCaseName ) rc += "["+this.#testCaseName+"]";
return rc + " line "+ this.#cursor.lineNo +" ";
return rc + " line "+ this.#cursor.lineNo;
}
reset(){
@@ -471,6 +660,106 @@ class TestScript {
}/*TestScript*/;
//! --close command
class CloseDbCommand extends Command {
process(t, ts, argv){
this.argcCheck(ts,argv,0,1);
let id;
if(argv.length>1){
const arg = argv[1];
if("all".equals(arg)){
t.closeAllDbs();
return;
}
else{
id = parseInt(arg);
}
}else{
id = t.currentDbId();
}
t.closeDb(id);
}
}
//! --column-names command
class ColumnNamesCommand extends Command {
process( st, ts, argv ){
this.argcCheck(ts,argv,1);
st.outputColumnNames( !!parseInt(argv[1]) );
}
}
//! --db command
class DbCommand extends Command {
process(t, ts, argv){
this.argcCheck(ts,argv,1);
t.currentDbId( parseInt(argv[1]) );
}
}
//! --open command
class OpenDbCommand extends Command {
#createIfNeeded = false;
constructor(createIfNeeded=false){
super();
this.#createIfNeeded = createIfNeeded;
}
process(t, ts, argv){
this.argcCheck(ts,argv,1);
t.openDb(argv[1], this.#createIfNeeded);
}
}
//! --new command
class NewDbCommand extends OpenDbCommand {
constructor(){ super(true); }
}
//! Placeholder dummy/no-op commands
class NoopCommand extends Command {
process(t, ts, argv){}
}
//! --null command
class NullCommand extends Command {
process(st, ts, argv){
this.argcCheck(ts,argv,1);
st.nullValue( argv[1] );
}
}
//! --print command
class PrintCommand extends Command {
process(st, ts, argv){
st.out(ts.getOutputPrefix(),': ');
if( 1==argv.length ){
st.out( st.getInputText() );
}else{
st.outln( Util.argvToString(argv) );
}
}
}
//! --testcase command
class TestCaseCommand extends Command {
process(tester, script, argv){
this.argcCheck(script, argv,1);
script.testCaseName(argv[1]);
tester.clearResultBuffer();
tester.clearInputBuffer();
}
}
//! --verbosity command
class VerbosityCommand extends Command {
process(t, ts, argv){
t.argcCheck(ts,argv,1);
ts.verbosity( parseInt(argv[1]) );
}
}
class CommandDispatcher {
static map = newObj();
@@ -478,8 +767,23 @@ class CommandDispatcher {
let rv = CommandDispatcher.map[name];
if( rv ) return rv;
switch(name){
//todo: map name to Command instance
case "testcase": rv = new TestCase(); break;
case "close": rv = new CloseDbCommand(); break;
case "column-names": rv = new ColumnNamesCommand(); break;
case "db": rv = new DbCommand(); break;
//case "glob": rv = new GlobCommand(); break;
//case "json": rv = new JsonCommand(); break;
//case "json-block": rv = new JsonBlockCommand(); break;
case "new": rv = new NewDbCommand(); break;
//case "notglob": rv = new NotGlobCommand(); break;
case "null": rv = new NullCommand(); break;
case "oom": rv = new NoopCommand(); break;
case "open": rv = new OpenDbCommand(); break;
case "print": rv = new PrintCommand(); break;
//case "result": rv = new ResultCommand(); break;
//case "run": rv = new RunCommand(); break;
//case "tableresult": rv = new TableResultCommand(); break;
case "testcase": rv = new TestCaseCommand(); break;
case "verbosity": rv = new VerbosityCommand(); break;
}
if( rv ){
CommandDispatcher.map[name] = rv;
@@ -506,7 +810,8 @@ const namespace = newObj({
TestScript,
TestScriptFailed,
UnknownCommand,
Util
Util,
sqlite3
});
export {namespace as default};