1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

fiddle: lots of generic refactoring, restructuring, and cleanup in the higher-level code. Added push-fiddle ext/fiddle/Makefile target to push the fiddle app to a remote server via rsync.

FossilOrigin-Name: ed19fef3459499abb0a4a010f368b4576d6e068d930c8480446ea677ac87c1c1
This commit is contained in:
stephan
2022-05-24 14:36:45 +00:00
parent 2f6a729d55
commit 400ee2ecef
9 changed files with 298 additions and 199 deletions

View File

@ -1,10 +1,31 @@
# This makefile exists primarily to simplify/speed up development from # This GNU makefile exists primarily to simplify/speed up development
# emacs. It is not part of the canonical build process. # from emacs. It is not part of the canonical build process.
default: default:
make -C ../.. wasm -e emcc_opt=-O0 make -C ../.. wasm -e emcc_opt=-O0
clean: clean:
make -C ../../ clean-wasm make -C ../../ clean-wasm
push-demo: demo_files = emscripten.css fiddle.html \
rsync -va fiddle*.js fiddle*.wasm fiddle.html *.css wh2:www/wh/sqlite3/. fiddle.js fiddle-module.js \
fiddle-module.wasm fiddle-worker.js
# demo_target is the remote destination for the fiddle app. It
# must be a [user@]HOST:/path for rsync.
# Note that the target "should probably" contain a symlink of
# index.html -> fiddle.html.
demo_target ?=
ifeq (,$(demo_target))
ifneq (,$(wildcard /home/stephan))
demo_target = wh2:www/wh/sqlite3/.
else ifneq (,$(wildcard /home/drh))
#demo_target = if appropriate, add that user@host:/path here
endif
endif
push-fiddle: $(demo_files)
@if [ x = "x$(demo_target)" ]; then \
echo "demo_target must be a [user@]HOST:/path for rsync"; \
exit 1; \
fi
rsync -va $(demo_files) $(demo_target)

View File

@ -132,35 +132,45 @@
return (arguments.length>1 ? arguments[0] : document) return (arguments.length>1 ? arguments[0] : document)
.querySelector(arguments[arguments.length-1]); .querySelector(arguments[arguments.length-1]);
}; };
const statusElement = E('#module-status');
const progressElement = E('#module-progress');
const spinnerElement = E('#module-spinner');
/** Handles status updates from the Module object. */
SF.addMsgHandler('module', function f(ev){ SF.addMsgHandler('module', function f(ev){
ev = ev.data; ev = ev.data;
if('status'!==ev.type){ if('status'!==ev.type){
console.warn("Unexpected module-type message:",ev); console.warn("Unexpected module-type message:",ev);
return; return;
} }
if(!f.ui){
f.ui = {
status: E('#module-status'),
progress: E('#module-progress'),
spinner: E('#module-spinner')
};
}
const msg = ev.data; const msg = ev.data;
progressElement.value = msg.step; if(f.ui.progres){
progressElement.max = msg.step + 1/*we don't know how many steps to expect*/; progress.value = msg.step;
progress.max = msg.step + 1/*we don't know how many steps to expect*/;
}
if(1==msg.step){ if(1==msg.step){
progressElement.hidden = false; f.ui.progress.classList.remove('hidden');
spinnerElement.hidden = false; f.ui.spinner.classList.remove('hidden');
} }
if(msg.text){ if(msg.text){
statusElement.classList.remove('hidden'); f.ui.status.classList.remove('hidden');
statusElement.innerText = msg.text; f.ui.status.innerText = msg.text;
}else{ }else{
progressElement.remove(); if(f.ui.progress){
spinnerElement.remove(); f.ui.progress.remove();
statusElement.classList.add('hidden'); f.ui.spinner.remove();
delete f.ui.progress;
delete f.ui.spinner;
}
f.ui.status.classList.add('hidden');
/* The module can post messages about fatal problems, /* The module can post messages about fatal problems,
e.g. an exit() being triggered or assertion failure, e.g. an exit() being triggered or assertion failure,
after the last "load" message has arrived, so after the last "load" message has arrived, so
leave the statusElement and message listener intact. */ leave f.ui.status and message listener intact. */
} }
}); });
@ -209,9 +219,9 @@
},false); },false);
/** To be called immediately before work is sent to the /** To be called immediately before work is sent to the
worker. Updates some UI elements. The 'working'/'end' worker. Updates some UI elements. The 'working'/'end'
event will apply the inverse, undoing the bits this event will apply the inverse, undoing the bits this
function does. This impl is not in the 'working'/'start' function does. This impl is not in the 'working'/'start'
event handler because that event is given to us event handler because that event is given to us
asynchronously _after_ we need to have performed this asynchronously _after_ we need to have performed this
work. work.
@ -242,13 +252,15 @@
}; };
SF.addMsgHandler('working',function f(ev){ SF.addMsgHandler('working',function f(ev){
if('start' === ev.data){ switch(ev.data){
/* See notes in preStartWork(). */ case 'start': /* See notes in preStartWork(). */; return;
}else if('end' === ev.data){ case 'end':
preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig; preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig;
btnShellExec.innerText = preStartWork._.btnLabel; btnShellExec.innerText = preStartWork._.btnLabel;
btnShellExec.removeAttribute('disabled'); btnShellExec.removeAttribute('disabled');
return;
} }
console.warn("Unhandled 'working' event:",ev.data);
}); });
/* For each checkbox with data-csstgt, set up a handler which /* For each checkbox with data-csstgt, set up a handler which

View File

@ -17,31 +17,33 @@
Note that this file is not named sqlite3.js because that file gets Note that this file is not named sqlite3.js because that file gets
generated by emscripten as the JS-glue counterpart of sqlite3.wasm. generated by emscripten as the JS-glue counterpart of sqlite3.wasm.
This code installs an object named self.sqlite3, where self is This code installs an object named self.Module.sqlite3, where self
expected to be either the global window or Worker object: is expected to be either the global window or Worker object and
Module is the object set up by the emscripten infrastructure. The
sqlite3 object looks like:
self.sqlite3 = { {
api: core WASM bindings of sqlite3 APIs, api: bindings for much of the core sqlite3 APIs,
SQLite3: high-level OO API wrapper SQLite3: high-level OO API wrapper
}; }
The way we export this module is not _really_ modern-JS-friendly The way we export this module is not _really_ modern-JS-friendly
because it exports a global symbol (which is admittedly not because it exports/relies on a global symbol (which is admittedly
ideal). Exporting it "cleanly," without introducing any global-scope not ideal). Exporting it "cleanly," without introducing any
symbols, requires using a module loader in all client code. As there global-scope symbols, requires using a module loader in all client
are several different approaches, none of which this developer is code. As there are several different approaches, none of which this
currently truly familiar with, the current approach will have to do developer is currently truly familiar with, the current approach
for the time being. will have to do for the time being.
Because using the low-level API properly requires some degree of Because using certain parts of the low-level API properly requires
WASM-related magic, it is not recommended that that API be used some degree of WASM-related magic, it is not recommended that that
as-is in client-level code. Rather, client code should use the API be used as-is in client-level code. Rather, client code should
higher-level OO API or write a custom wrapper on top of the use the higher-level OO API or write a custom wrapper on top of the
lower-level API. lower-level API. In short, using any C-style APIs which take
pointers-to-pointer arguments require WASM-specific interfaces
This file installs namespace.sqlite3, where namespace is `self`, installed by emcscripten-generated code. Those which take or return
meaning either the global window or worker, depending on where this only integers, doubles, strings, or "plain" pointers to db or
is loaded from. statement objects can be used in a straightforward manner.
# Goals and Non-goals of this API # Goals and Non-goals of this API
@ -60,13 +62,16 @@
can be interacted with, but keeping the DB operations out of the can be interacted with, but keeping the DB operations out of the
UI thread is generally desirable. UI thread is generally desirable.
- Insofar as possible, support client-side storage using JS
filesystem APIs. As of this writing, such things are still very
much TODO.
Non-goals: Non-goals:
- As WASM is a web-based technology and UTF-8 is the King of - As WASM is a web-centric technology and UTF-8 is the King of
Encodings in that realm, there are no plans to support the Encodings in that realm, there are no current plans to support the
UTF16-related APIs will not be. They would add a complication to UTF16-related APIs. They would add a complication to the bindings
the bindings for no appreciable benefit. for no appreciable benefit.
- Supporting old or niche-market platforms. WASM is built for a - Supporting old or niche-market platforms. WASM is built for a
modern web and requires modern platforms. modern web and requires modern platforms.
@ -247,8 +252,6 @@
throw new Error(Array.prototype.join.call(arguments, ' ')); throw new Error(Array.prototype.join.call(arguments, ' '));
}; };
const S/*convenience alias*/ = api;
/** /**
The DB class wraps a sqlite3 db handle. The DB class wraps a sqlite3 db handle.
*/ */
@ -258,7 +261,7 @@
toss("TODO: support blob image of db here."); toss("TODO: support blob image of db here.");
} }
setValue(pPtrArg, 0, "i32"); setValue(pPtrArg, 0, "i32");
this.checkRc(S.sqlite3_open(name, pPtrArg)); this.checkRc(api.sqlite3_open(name, pPtrArg));
this._pDb = getValue(pPtrArg, "i32"); this._pDb = getValue(pPtrArg, "i32");
this.filename = name; this.filename = name;
this._statements = {/*map of open Stmt _pointers_ to Stmt*/}; this._statements = {/*map of open Stmt _pointers_ to Stmt*/};
@ -291,8 +294,8 @@
} }
this.db = arguments[0]; this.db = arguments[0];
this._pStmt = arguments[1]; this._pStmt = arguments[1];
this.columnCount = S.sqlite3_column_count(this._pStmt); this.columnCount = api.sqlite3_column_count(this._pStmt);
this.parameterCount = S.sqlite3_bind_parameter_count(this._pStmt); this.parameterCount = api.sqlite3_bind_parameter_count(this._pStmt);
this._allocs = [/*list of alloc'd memory blocks for bind() values*/] this._allocs = [/*list of alloc'd memory blocks for bind() values*/]
}; };
@ -370,7 +373,7 @@
checkRc: function(sqliteResultCode){ checkRc: function(sqliteResultCode){
if(!sqliteResultCode) return this; if(!sqliteResultCode) return this;
toss("sqlite result code",sqliteResultCode+":", toss("sqlite result code",sqliteResultCode+":",
S.sqlite3_errmsg(this._pDb) || "Unknown db error."); api.sqlite3_errmsg(this._pDb) || "Unknown db error.");
}, },
/** /**
Finalizes all open statements and closes this database Finalizes all open statements and closes this database
@ -388,7 +391,7 @@
Object.values(this._udfs).forEach(Module.removeFunction); Object.values(this._udfs).forEach(Module.removeFunction);
delete this._udfs; delete this._udfs;
delete this._statements; delete this._statements;
S.sqlite3_close_v2(this._pDb); api.sqlite3_close_v2(this._pDb);
delete this._pDb; delete this._pDb;
} }
}, },
@ -401,7 +404,7 @@
a name of `main`. a name of `main`.
*/ */
fileName: function(dbName){ fileName: function(dbName){
return S.sqlite3_db_filename(affirmDbOpen(this)._pDb, dbName||"main"); return api.sqlite3_db_filename(affirmDbOpen(this)._pDb, dbName||"main");
}, },
/** /**
Compiles the given SQL and returns a prepared Stmt. This is Compiles the given SQL and returns a prepared Stmt. This is
@ -410,7 +413,7 @@
prepare: function(sql){ prepare: function(sql){
affirmDbOpen(this); affirmDbOpen(this);
setValue(pPtrArg,0,"i32"); setValue(pPtrArg,0,"i32");
this.checkRc(S.sqlite3_prepare_v2(this._pDb, sql, -1, pPtrArg, null)); this.checkRc(api.sqlite3_prepare_v2(this._pDb, sql, -1, pPtrArg, null));
const pStmt = getValue(pPtrArg, "i32"); const pStmt = getValue(pPtrArg, "i32");
if(!pStmt) toss("Empty SQL is not permitted."); if(!pStmt) toss("Empty SQL is not permitted.");
const stmt = new Stmt(this, pStmt, BindTypes); const stmt = new Stmt(this, pStmt, BindTypes);
@ -533,14 +536,14 @@
while(getValue(pSql, "i8")){ while(getValue(pSql, "i8")){
setValue(pPtrArg, 0, "i32"); setValue(pPtrArg, 0, "i32");
setValue(pzTail, 0, "i32"); setValue(pzTail, 0, "i32");
this.checkRc(S.sqlite3_prepare_v2_sqlptr( this.checkRc(api.sqlite3_prepare_v2_sqlptr(
this._pDb, pSql, -1, pPtrArg, pzTail this._pDb, pSql, -1, pPtrArg, pzTail
)); ));
const pStmt = getValue(pPtrArg, "i32"); const pStmt = getValue(pPtrArg, "i32");
pSql = getValue(pzTail, "i32"); pSql = getValue(pzTail, "i32");
if(!pStmt) continue; if(!pStmt) continue;
if(opt.saveSql){ if(opt.saveSql){
opt.saveSql.push(S.sqlite3_sql(pStmt).trim()); opt.saveSql.push(api.sqlite3_sql(pStmt).trim());
} }
stmt = new Stmt(this, pStmt, BindTypes); stmt = new Stmt(this, pStmt, BindTypes);
if(bind && stmt.parameterCount){ if(bind && stmt.parameterCount){
@ -653,18 +656,18 @@
const tgt = []; const tgt = [];
for(i = 0; i < argc; ++i){ for(i = 0; i < argc; ++i){
pVal = getValue(pArgv + (4 * i), "i32"); pVal = getValue(pArgv + (4 * i), "i32");
valType = S.sqlite3_value_type(pVal); valType = api.sqlite3_value_type(pVal);
switch(valType){ switch(valType){
case S.SQLITE_INTEGER: case api.SQLITE_INTEGER:
case S.SQLITE_FLOAT: case api.SQLITE_FLOAT:
arg = S.sqlite3_value_double(pVal); arg = api.sqlite3_value_double(pVal);
break; break;
case SQLITE_TEXT: case SQLITE_TEXT:
arg = S.sqlite3_value_text(pVal); arg = api.sqlite3_value_text(pVal);
break; break;
case SQLITE_BLOB:{ case SQLITE_BLOB:{
const n = S.sqlite3_value_bytes(ptr); const n = api.sqlite3_value_bytes(ptr);
const pBlob = S.sqlite3_value_blob(ptr); const pBlob = api.sqlite3_value_blob(ptr);
arg = new Uint8Array(n); arg = new Uint8Array(n);
let i; let i;
for(i = 0; i < n; ++i) arg[i] = HEAP8[pBlob+i]; for(i = 0; i < n; ++i) arg[i] = HEAP8[pBlob+i];
@ -680,25 +683,25 @@
f._setResult = function(pCx, val){ f._setResult = function(pCx, val){
switch(typeof val) { switch(typeof val) {
case 'boolean': case 'boolean':
S.sqlite3_result_int(pCx, val ? 1 : 0); api.sqlite3_result_int(pCx, val ? 1 : 0);
break; break;
case 'number': { case 'number': {
(isInt32(val) (isInt32(val)
? S.sqlite3_result_int ? api.sqlite3_result_int
: S.sqlite3_result_double)(pCx, val); : api.sqlite3_result_double)(pCx, val);
break; break;
} }
case 'string': case 'string':
S.sqlite3_result_text(pCx, val, -1, api.sqlite3_result_text(pCx, val, -1,
-1/*==SQLITE_TRANSIENT*/); -1/*==SQLITE_TRANSIENT*/);
break; break;
case 'object': case 'object':
if(null===val) { if(null===val) {
S.sqlite3_result_null(pCx); api.sqlite3_result_null(pCx);
break; break;
}else if(undefined!==val.length){ }else if(undefined!==val.length){
const pBlob = Module.allocate(val, ALLOC_NORMAL); const pBlob = Module.allocate(val, ALLOC_NORMAL);
S.sqlite3_result_blob(pCx, pBlob, val.length, -1/*==SQLITE_TRANSIENT*/); api.sqlite3_result_blob(pCx, pBlob, val.length, -1/*==SQLITE_TRANSIENT*/);
Module._free(blobptr); Module._free(blobptr);
break; break;
} }
@ -712,20 +715,20 @@
try{ try{
f._setResult(pCx, callback.apply(null, f._extractArgs(argc, pArgv))); f._setResult(pCx, callback.apply(null, f._extractArgs(argc, pArgv)));
}catch(e){ }catch(e){
S.sqlite3_result_error(pCx, e.message, -1); api.sqlite3_result_error(pCx, e.message, -1);
} }
}; };
const pUdf = Module.addFunction(wrapper, "viii"); const pUdf = Module.addFunction(wrapper, "viii");
let fFlags = 0; let fFlags = 0;
if(getOwnOption(opt, 'deterministic')) fFlags |= S.SQLITE_DETERMINISTIC; if(getOwnOption(opt, 'deterministic')) fFlags |= api.SQLITE_DETERMINISTIC;
if(getOwnOption(opt, 'directOnly')) fFlags |= S.SQLITE_DIRECTONLY; if(getOwnOption(opt, 'directOnly')) fFlags |= api.SQLITE_DIRECTONLY;
if(getOwnOption(opt, 'innocuous')) fFlags |= S.SQLITE_INNOCUOUS; if(getOwnOption(opt, 'innocuous')) fFlags |= api.SQLITE_INNOCUOUS;
name = name.toLowerCase(); name = name.toLowerCase();
try { try {
this.checkRc(S.sqlite3_create_function_v2( this.checkRc(api.sqlite3_create_function_v2(
this._pDb, name, this._pDb, name,
(opt.hasOwnProperty('arity') ? +opt.arity : callback.length), (opt.hasOwnProperty('arity') ? +opt.arity : callback.length),
S.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf, api.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
}catch(e){ }catch(e){
Module.removeFunction(pUdf); Module.removeFunction(pUdf);
@ -802,7 +805,7 @@
*/ */
const affirmParamIndex = function(stmt,key){ const affirmParamIndex = function(stmt,key){
const n = ('number'===typeof key) const n = ('number'===typeof key)
? key : S.sqlite3_bind_parameter_index(stmt._pStmt, key); ? key : api.sqlite3_bind_parameter_index(stmt._pStmt, key);
if(0===n || (n===key && (n!==(n|0)/*floating point*/))){ if(0===n || (n===key && (n!==(n|0)/*floating point*/))){
toss("Invalid bind() parameter name: "+key); toss("Invalid bind() parameter name: "+key);
} }
@ -853,7 +856,7 @@
const bytes = intArrayFromString(val,true); const bytes = intArrayFromString(val,true);
const pStr = Module.allocate(bytes, ALLOC_NORMAL); const pStr = Module.allocate(bytes, ALLOC_NORMAL);
stmt._allocs.push(pStr); stmt._allocs.push(pStr);
const func = asBlob ? S.sqlite3_bind_blob : S.sqlite3_bind_text; const func = asBlob ? api.sqlite3_bind_blob : api.sqlite3_bind_text;
return func(stmt._pStmt, ndx, pStr, bytes.length, 0); return func(stmt._pStmt, ndx, pStr, bytes.length, 0);
} }
}; };
@ -863,7 +866,7 @@
let rc = 0; let rc = 0;
switch((null===val || undefined===val) ? BindTypes.null : bindType){ switch((null===val || undefined===val) ? BindTypes.null : bindType){
case BindTypes.null: case BindTypes.null:
rc = S.sqlite3_bind_null(stmt._pStmt, ndx); rc = api.sqlite3_bind_null(stmt._pStmt, ndx);
break; break;
case BindTypes.string:{ case BindTypes.string:{
rc = f._.string(stmt, ndx, val, false); rc = f._.string(stmt, ndx, val, false);
@ -871,15 +874,15 @@
} }
case BindTypes.number: { case BindTypes.number: {
const m = (isInt32(val) const m = (isInt32(val)
? S.sqlite3_bind_int ? api.sqlite3_bind_int
/*It's illegal to bind a 64-bit int /*It's illegal to bind a 64-bit int
from here*/ from here*/
: S.sqlite3_bind_double); : api.sqlite3_bind_double);
rc = m(stmt._pStmt, ndx, val); rc = m(stmt._pStmt, ndx, val);
break; break;
} }
case BindTypes.boolean: case BindTypes.boolean:
rc = S.sqlite3_bind_int(stmt._pStmt, ndx, val ? 1 : 0); rc = api.sqlite3_bind_int(stmt._pStmt, ndx, val ? 1 : 0);
break; break;
case BindTypes.blob: { case BindTypes.blob: {
if('string'===typeof val){ if('string'===typeof val){
@ -892,7 +895,7 @@
} }
const pBlob = Module.allocate(val, ALLOC_NORMAL); const pBlob = Module.allocate(val, ALLOC_NORMAL);
stmt._allocs.push(pBlob); stmt._allocs.push(pBlob);
rc = S.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0); rc = api.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0);
} }
} }
default: toss("Unsupported bind() argument type."); default: toss("Unsupported bind() argument type.");
@ -923,7 +926,7 @@
affirmUnlocked(this,'finalize()'); affirmUnlocked(this,'finalize()');
freeBindMemory(this); freeBindMemory(this);
delete this.db._statements[this._pStmt]; delete this.db._statements[this._pStmt];
S.sqlite3_finalize(this._pStmt); api.sqlite3_finalize(this._pStmt);
delete this.columnCount; delete this.columnCount;
delete this.parameterCount; delete this.parameterCount;
delete this._pStmt; delete this._pStmt;
@ -937,7 +940,7 @@
freeBindMemory( freeBindMemory(
affirmUnlocked(affirmStmtOpen(this), 'clearBindings()') affirmUnlocked(affirmStmtOpen(this), 'clearBindings()')
); );
S.sqlite3_clear_bindings(this._pStmt); api.sqlite3_clear_bindings(this._pStmt);
this._mayGet = false; this._mayGet = false;
return this; return this;
}, },
@ -953,7 +956,7 @@
reset: function(alsoClearBinds){ reset: function(alsoClearBinds){
affirmUnlocked(this,'reset()'); affirmUnlocked(this,'reset()');
if(alsoClearBinds) this.clearBindings(); if(alsoClearBinds) this.clearBindings();
S.sqlite3_reset(affirmStmtOpen(this)._pStmt); api.sqlite3_reset(affirmStmtOpen(this)._pStmt);
this._mayGet = false; this._mayGet = false;
return this; return this;
}, },
@ -1098,14 +1101,14 @@
*/ */
step: function(){ step: function(){
affirmUnlocked(this, 'step()'); affirmUnlocked(this, 'step()');
const rc = S.sqlite3_step(affirmStmtOpen(this)._pStmt); const rc = api.sqlite3_step(affirmStmtOpen(this)._pStmt);
switch(rc){ switch(rc){
case S.SQLITE_DONE: return this._mayGet = false; case api.SQLITE_DONE: return this._mayGet = false;
case S.SQLITE_ROW: return this._mayGet = true; case api.SQLITE_ROW: return this._mayGet = true;
default: default:
this._mayGet = false; this._mayGet = false;
console.warn("sqlite3_step() rc=",rc,"SQL =", console.warn("sqlite3_step() rc=",rc,"SQL =",
S.sqlite3_sql(this._pStmt)); api.sqlite3_sql(this._pStmt));
this.db.checkRc(rc); this.db.checkRc(rc);
}; };
}, },
@ -1155,27 +1158,27 @@
}else if(ndx && 'object'===typeof ndx){ }else if(ndx && 'object'===typeof ndx){
let i = 0; let i = 0;
while(i<this.columnCount){ while(i<this.columnCount){
ndx[S.sqlite3_column_name(this._pStmt,i)] = this.get(i++); ndx[api.sqlite3_column_name(this._pStmt,i)] = this.get(i++);
} }
return ndx; return ndx;
} }
affirmColIndex(this, ndx); affirmColIndex(this, ndx);
switch(undefined===asType switch(undefined===asType
? S.sqlite3_column_type(this._pStmt, ndx) ? api.sqlite3_column_type(this._pStmt, ndx)
: asType){ : asType){
case S.SQLITE_NULL: return null; case api.SQLITE_NULL: return null;
case S.SQLITE_INTEGER:{ case api.SQLITE_INTEGER:{
return 0 | S.sqlite3_column_double(this._pStmt, ndx); return 0 | api.sqlite3_column_double(this._pStmt, ndx);
/* ^^^^^^^^ strips any fractional part and handles /* ^^^^^^^^ strips any fractional part and handles
handles >32bits */ handles >32bits */
} }
case S.SQLITE_FLOAT: case api.SQLITE_FLOAT:
return S.sqlite3_column_double(this._pStmt, ndx); return api.sqlite3_column_double(this._pStmt, ndx);
case S.SQLITE_TEXT: case api.SQLITE_TEXT:
return S.sqlite3_column_text(this._pStmt, ndx); return api.sqlite3_column_text(this._pStmt, ndx);
case S.SQLITE_BLOB: { case api.SQLITE_BLOB: {
const n = S.sqlite3_column_bytes(this._pStmt, ndx); const n = api.sqlite3_column_bytes(this._pStmt, ndx);
const ptr = S.sqlite3_column_blob(this._pStmt, ndx); const ptr = api.sqlite3_column_blob(this._pStmt, ndx);
const rc = new Uint8Array(n); const rc = new Uint8Array(n);
for(let i = 0; i < n; ++i) rc[i] = HEAP8[ptr + i]; for(let i = 0; i < n; ++i) rc[i] = HEAP8[ptr + i];
return rc; return rc;
@ -1187,16 +1190,16 @@
}, },
/** Equivalent to get(ndx) but coerces the result to an /** Equivalent to get(ndx) but coerces the result to an
integer. */ integer. */
getInt: function(ndx){return this.get(ndx,S.SQLITE_INTEGER)}, getInt: function(ndx){return this.get(ndx,api.SQLITE_INTEGER)},
/** Equivalent to get(ndx) but coerces the result to a /** Equivalent to get(ndx) but coerces the result to a
float. */ float. */
getFloat: function(ndx){return this.get(ndx,S.SQLITE_FLOAT)}, getFloat: function(ndx){return this.get(ndx,api.SQLITE_FLOAT)},
/** Equivalent to get(ndx) but coerces the result to a /** Equivalent to get(ndx) but coerces the result to a
string. */ string. */
getString: function(ndx){return this.get(ndx,S.SQLITE_TEXT)}, getString: function(ndx){return this.get(ndx,api.SQLITE_TEXT)},
/** Equivalent to get(ndx) but coerces the result to a /** Equivalent to get(ndx) but coerces the result to a
Uint8Array. */ Uint8Array. */
getBlob: function(ndx){return this.get(ndx,S.SQLITE_BLOB)}, getBlob: function(ndx){return this.get(ndx,api.SQLITE_BLOB)},
/** /**
A convenience wrapper around get() which fetches the value A convenience wrapper around get() which fetches the value
as a string and then, if it is not null, passes it to as a string and then, if it is not null, passes it to
@ -1205,16 +1208,17 @@
string, on the other hand, will trigger an exception. string, on the other hand, will trigger an exception.
*/ */
getJSON: function(ndx){ getJSON: function(ndx){
const s = this.get(ndx, S.SQLITE_STRING); const s = this.get(ndx, api.SQLITE_STRING);
return null===s ? s : JSON.parse(s); return null===s ? s : JSON.parse(s);
}, },
/** /**
Returns the result column name of the given index, or Returns the result column name of the given index, or
throws if index is out of bounds or this statement has been throws if index is out of bounds or this statement has been
finalized. finalized. This can be used without having run step()
first.
*/ */
getColumnName: function(ndx){ getColumnName: function(ndx){
return S.sqlite3_column_name( return api.sqlite3_column_name(
affirmColIndex(affirmStmtOpen(this),ndx)._pStmt, ndx affirmColIndex(affirmStmtOpen(this),ndx)._pStmt, ndx
); );
}, },
@ -1230,7 +1234,7 @@
affirmColIndex(affirmStmtOpen(this),0); affirmColIndex(affirmStmtOpen(this),0);
if(!tgt) tgt = []; if(!tgt) tgt = [];
for(let i = 0; i < this.columnCount; ++i){ for(let i = 0; i < this.columnCount; ++i){
tgt.push(S.sqlite3_column_name(this._pStmt, i)); tgt.push(api.sqlite3_column_name(this._pStmt, i));
} }
return tgt; return tgt;
}, },
@ -1242,7 +1246,7 @@
*/ */
getParamIndex: function(name){ getParamIndex: function(name){
return (affirmStmtOpen(this).parameterCount return (affirmStmtOpen(this).parameterCount
? S.sqlite3_bind_parameter_index(this._pStmt, name) ? api.sqlite3_bind_parameter_index(this._pStmt, name)
: undefined); : undefined);
} }
}/*Stmt.prototype*/; }/*Stmt.prototype*/;
@ -1250,7 +1254,7 @@
/** OO binding's namespace. */ /** OO binding's namespace. */
const SQLite3 = { const SQLite3 = {
version: { version: {
lib: S.sqlite3_libversion(), lib: api.sqlite3_libversion(),
ooApi: "0.0.1" ooApi: "0.0.1"
}, },
DB, DB,
@ -1293,7 +1297,7 @@
} }
const rc = {}, ov = [0,0]; const rc = {}, ov = [0,0];
let i = 0, k; let i = 0, k;
while((k = S.sqlite3_compileoption_get(i++))){ while((k = api.sqlite3_compileoption_get(i++))){
f._opt(k,ov); f._opt(k,ov);
rc[ov[0]] = ov[1]; rc[ov[0]] = ov[1];
} }
@ -1302,19 +1306,19 @@
else if(Array.isArray(optName)){ else if(Array.isArray(optName)){
const rc = {}; const rc = {};
optName.forEach((v)=>{ optName.forEach((v)=>{
rc[v] = S.sqlite3_compileoption_used(v); rc[v] = api.sqlite3_compileoption_used(v);
}); });
return rc; return rc;
} }
else if('object' === typeof optName){ else if('object' === typeof optName){
Object.keys(optName).forEach((k)=> { Object.keys(optName).forEach((k)=> {
optName[k] = S.sqlite3_compileoption_used(k); optName[k] = api.sqlite3_compileoption_used(k);
}); });
return optName; return optName;
} }
return ( return (
'string'===typeof optName 'string'===typeof optName
) ? !!S.sqlite3_compileoption_used(optName) : false; ) ? !!api.sqlite3_compileoption_used(optName) : false;
} }
}; };
@ -1322,4 +1326,4 @@
api: api, api: api,
SQLite3 SQLite3
}; };
})(self/*worker or window*/); })(self/*worker or window*/.Module);

View File

@ -26,9 +26,6 @@
}; };
/* emscripten-related bits... */ /* emscripten-related bits... */
const statusElement = E('#module-status');
const progressElement = E('#module-progress');
const spinnerElement = E('#module-spinner');
self.Module = { self.Module = {
/* ^^^ cannot declare that const because sqlite3.js /* ^^^ cannot declare that const because sqlite3.js
(auto-generated) includes a decl for it and runs in this (auto-generated) includes a decl for it and runs in this
@ -43,25 +40,33 @@
console.error.apply(console, Array.prototype.slice.call(arguments)); console.error.apply(console, Array.prototype.slice.call(arguments));
}, },
setStatus: function f(text){ setStatus: function f(text){
if(!f.last) f.last = { time: Date.now(), text: '' }; if(!f.last){
if(text === f.last.text) return; f.last = { text: '', step: 0 };
const m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); f.ui = {
const now = Date.now(); status: E('#module-status'),
if(m && now - f.last.time < 30) return; // if this is a progress update, skip it if too soon progress: E('#module-progress'),
f.last.time = now; spinner: E('#module-spinner')
f.last.text = text; };
if(m) { }
text = m[1]; if(text === f.last.text) return;
progressElement.value = parseInt(m[2])*100; f.last.text = text;
progressElement.max = parseInt(m[4])*100; if(f.ui.progress){
progressElement.hidden = false; f.ui.progress.value = f.last.step;
spinnerElement.hidden = false; f.ui.progress.max = f.last.step + 1;
} else { }
progressElement.remove(); ++f.last.step;
if(!text) spinnerElement.remove(); if(text) {
f.ui.status.classList.remove('hidden');
f.ui.status.innerText = text;
}else{
if(f.ui.progress){
f.ui.progress.remove();
f.ui.spinner.remove();
delete f.ui.progress;
delete f.ui.spinner;
}
f.ui.status.classList.add('hidden');
} }
if(text) statusElement.innerText = text;
else statusElement.remove();
}, },
totalDependencies: 0, totalDependencies: 0,
monitorRunDependencies: function(left) { monitorRunDependencies: function(left) {
@ -71,16 +76,33 @@
+ '/' + this.totalDependencies + ')') + '/' + this.totalDependencies + ')')
: 'All downloads complete.'); : 'All downloads complete.');
}, },
/* Loads sqlite3-api.js and calls the given callback (if /**
provided), passing it an object which contains the sqlite3 Loads sqlite3-api.js and calls the given callback (if
and SQLite3 modules. Whether this is synchronous or async provided), passing it an object:
depends on whether it's run in the main thread (async) or a
worker (synchronous). */ {
loadSqliteAPI: function(callback){ api:sqlite3_c-like API wrapper,
SQLite3: OO wrapper
}
Whether this is synchronous or async depends on whether
it's run in the main thread (async) or a worker
(synchronous).
If called after the module has been loaded, it uses a
cached reference, noting that multiple async calls may end
up loading it multiple times.
*/
loadSqliteAPI: function f(callback){
const namespace = self.Module;
if(namespace.sqlite3){
if(callback) callback(namespace.sqlite3);
return;
}
const theScript = 'sqlite3-api.js'; const theScript = 'sqlite3-api.js';
if(self.importScripts){/*worker*/ if(self.importScripts){/*worker*/
importScripts(theScript); importScripts(theScript);
if(callback) callback(self.sqlite3); if(callback) callback(namespace.sqlite3);
}else{/*main thread*/ }else{/*main thread*/
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
const script = document.createElement('script'); const script = document.createElement('script');
@ -90,8 +112,7 @@
script.async = true; script.async = true;
script.src = theScript; script.src = theScript;
}).then(() => { }).then(() => {
if(callback) callback({sqlite3:self.sqlite3, if(callback) callback(namespace.sqlite3);
SQLite3:self.SQLite3});
}); });
} }
} }

31
ext/fiddle/testing.css Normal file
View File

@ -0,0 +1,31 @@
textarea {
font-family: monospace;
}
header {
font-size: 130%;
font-weight: bold;
}
.hidden, .initially-hidden {
position: absolute !important;
opacity: 0 !important;
pointer-events: none !important;
display: none !important;
}
fieldset.options {
font-size: 75%;
}
fieldset > legend {
padding: 0 0.5em;
}
span.labeled-input {
padding: 0.25em;
margin: 0.25em 0.5em;
border-radius: 0.25em;
white-space: nowrap;
background: #0002;
}
.center { text-align: center; }
.error {
color: red;
background-color: yellow;
}

View File

@ -4,6 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="emscripten.css"/> <link rel="stylesheet" href="emscripten.css"/>
<link rel="stylesheet" href="testing.css"/>
<title>sqlite3-api.js tests</title> <title>sqlite3-api.js tests</title>
<style></style> <style></style>
</head> </head>

View File

@ -12,27 +12,15 @@
A basic test script for sqlite3-api.js. A basic test script for sqlite3-api.js.
*/ */
(function(){
const mainTest1 = function(namespace){
const T = self.SqliteTestUtil; const T = self.SqliteTestUtil;
T.assert(Module._free instanceof Function).
assert(Module.allocate instanceof Function).
assert(Module.addFunction instanceof Function).
assert(Module.removeFunction instanceof Function);
const S = namespace.sqlite3.api;
const oo = namespace.sqlite3.SQLite3;
console.log("Loaded module:",S.sqlite3_libversion(),
S.sqlite3_sourceid());
const db = new oo.DB();
const log = console.log.bind(console); const log = console.log.bind(console);
try {
const test1 = function(db,api){
log("Basic sanity tests...");
T.assert(db._pDb); T.assert(db._pDb);
log("DB:",db.filename);
log("Build options:",oo.compileOptionUsed());
let st = db.prepare("select 3 as a"); let st = db.prepare("select 3 as a");
log("statement =",st); //log("statement =",st);
T.assert(st._pStmt) T.assert(st._pStmt)
.assert(!st._mayGet) .assert(!st._mayGet)
.assert('a' === st.getColumnName(0)) .assert('a' === st.getColumnName(0))
@ -43,14 +31,14 @@ const mainTest1 = function(namespace){
.assert(true===st.step()) .assert(true===st.step())
.assert(3 === st.get(0)) .assert(3 === st.get(0))
.mustThrow(()=>st.get(1)) .mustThrow(()=>st.get(1))
.mustThrow(()=>st.get(0,~S.SQLITE_INTEGER)) .mustThrow(()=>st.get(0,~api.SQLITE_INTEGER))
.assert(3 === st.get(0,S.SQLITE_INTEGER)) .assert(3 === st.get(0,api.SQLITE_INTEGER))
.assert(3 === st.getInt(0)) .assert(3 === st.getInt(0))
.assert('3' === st.get(0,S.SQLITE_TEXT)) .assert('3' === st.get(0,api.SQLITE_TEXT))
.assert('3' === st.getString(0)) .assert('3' === st.getString(0))
.assert(3.0 === st.get(0,S.SQLITE_FLOAT)) .assert(3.0 === st.get(0,api.SQLITE_FLOAT))
.assert(3.0 === st.getFloat(0)) .assert(3.0 === st.getFloat(0))
.assert(st.get(0,S.SQLITE_BLOB) instanceof Uint8Array) .assert(st.get(0,api.SQLITE_BLOB) instanceof Uint8Array)
.assert(st.getBlob(0) instanceof Uint8Array) .assert(st.getBlob(0) instanceof Uint8Array)
.assert(3 === st.get([])[0]) .assert(3 === st.get([])[0])
.assert(3 === st.get({}).a) .assert(3 === st.get({}).a)
@ -73,7 +61,7 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
bind: [5,6] bind: [5,6]
}); });
T.assert(2 === list.length); T.assert(2 === list.length);
log("Exec'd SQL:", list); //log("Exec'd SQL:", list);
let counter = 0, colNames = []; let counter = 0, colNames = [];
db.exec("SELECT a a, b b FROM t",{ db.exec("SELECT a a, b b FROM t",{
rowMode: 'object', rowMode: 'object',
@ -95,7 +83,9 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
} }
}); });
T.assert(6 === counter); T.assert(6 === counter);
};
const testUDF = function(db){
log("Testing UDF..."); log("Testing UDF...");
db.createFunction("foo",function(a,b){return a+b}); db.createFunction("foo",function(a,b){return a+b});
T.assert(7===db.selectValue("select foo(3,4)")). T.assert(7===db.selectValue("select foo(3,4)")).
@ -110,6 +100,8 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
return rc; return rc;
} }
}); });
log("Testing DB::selectValue() w/ UDF...");
T.assert(0===db.selectValue("select bar()")). T.assert(0===db.selectValue("select bar()")).
assert(1===db.selectValue("select bar(1)")). assert(1===db.selectValue("select bar(1)")).
assert(3===db.selectValue("select bar(1,2)")). assert(3===db.selectValue("select bar(1,2)")).
@ -120,18 +112,34 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
assert(null === db.selectValue("select ?",null)). assert(null === db.selectValue("select ?",null)).
assert(null === db.selectValue("select ?",[null])). assert(null === db.selectValue("select ?",[null])).
assert(null === db.selectValue("select $a",{$a:null})); assert(null === db.selectValue("select $a",{$a:null}));
};
}finally{
db.close();
}
log("Total Test count:",T.counter);
};
self/*window or worker*/.Module.postRun.push(function(theModule){ const runTests = function(namespace){
/** Use a timeout so that we are (hopefully) out from under the T.assert(Module._free instanceof Function).
module init stack when our setup gets run. */ assert(Module.allocate instanceof Function).
assert(Module.addFunction instanceof Function).
setTimeout(function(){ assert(Module.removeFunction instanceof Function);
theModule.loadSqliteAPI(mainTest1); const api = namespace.api;
},0); const oo = namespace.SQLite3;
}); console.log("Loaded module:",api.sqlite3_libversion(),
api.sqlite3_sourceid());
log("Build options:",oo.compileOptionUsed());
const db = new oo.DB();
try {
log("DB:",db.filename);
[
test1, testUDF
].forEach((f)=>f(db, api));
}finally{
db.close();
}
log("Total Test count:",T.counter);
};
self.Module.postRun.push(function(theModule){
/** Use a timeout so that we are (hopefully) out from under the
module init stack when our setup gets run. Just on principle,
not because we _need_ to be. */
setTimeout(()=>theModule.loadSqliteAPI(runTests), 0);
});
})(self/*window or worker*/);

View File

@ -1,5 +1,5 @@
C wasm/JS:\sminor\sdoc\supdates,\scorrected\sbind()ing\sof\sthe\sundefined\svalue\sto\sbehave\sas\sdocumented,\sremoved\ssome\ssuperfluous\scode. C fiddle:\slots\sof\sgeneric\srefactoring,\srestructuring,\sand\scleanup\sin\sthe\shigher-level\scode.\sAdded\spush-fiddle\sext/fiddle/Makefile\starget\sto\spush\sthe\sfiddle\sapp\sto\sa\sremote\sserver\svia\srsync.
D 2022-05-24T01:15:21.052 D 2022-05-24T14:36:45.563
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -58,17 +58,18 @@ F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be
F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 487fc7c83d45c48326f731c89162ed17ab15767e5efede8999d7d6c6e2d04c0f F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 487fc7c83d45c48326f731c89162ed17ab15767e5efede8999d7d6c6e2d04c0f
F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3 07b573a1830cb2d38ed347cf2a4139ec3b9c0f69748da6a2d8356b426c807694 F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3 07b573a1830cb2d38ed347cf2a4139ec3b9c0f69748da6a2d8356b426c807694
F ext/fiddle/EXPORTED_RUNTIME_METHODS ff64aea52779b0d4a838268275fe02adf6f2fdf4d9ce21c22d104bf3d7597398 F ext/fiddle/EXPORTED_RUNTIME_METHODS ff64aea52779b0d4a838268275fe02adf6f2fdf4d9ce21c22d104bf3d7597398
F ext/fiddle/Makefile 9277c73e208b9c8093659256c9f07409c877e366480c7c22ec545ee345451d95 F ext/fiddle/Makefile 8eb51a07b4ff7e5684ca829906233c07d0dccb5d14a9d8b4ec737a2a6f3f0d7e
F ext/fiddle/SqliteTestUtil.js e3094833660a6ddd40766b802901b5861b37f0b89c6c577ee0ce4c9d36399e61 F ext/fiddle/SqliteTestUtil.js e3094833660a6ddd40766b802901b5861b37f0b89c6c577ee0ce4c9d36399e61
F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
F ext/fiddle/fiddle-worker.js e87c17070b979bd057a6849332f2a86660a4255ff7f1b6671e3e6026182ffd5a F ext/fiddle/fiddle-worker.js e87c17070b979bd057a6849332f2a86660a4255ff7f1b6671e3e6026182ffd5a
F ext/fiddle/fiddle.html 657c6c3f860c322fba3c69fa4f7a1209e2d2ce44b4bc65a3e154e3a97c047a7c F ext/fiddle/fiddle.html 657c6c3f860c322fba3c69fa4f7a1209e2d2ce44b4bc65a3e154e3a97c047a7c
F ext/fiddle/fiddle.js 68f5bb45fc1ae7f8ae3f6b85f465257db514d12bf50ec492259685178c452a88 F ext/fiddle/fiddle.js 0263a1ebf7e09ecd8b37ff8e00b9ba27c543b65b6c3dbf2f9def90e6c71c4580
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
F ext/fiddle/sqlite3-api.js 3f41887a66d620ae506fea4a735d909c3dc0023045265736958de6d3016fbfc9 F ext/fiddle/sqlite3-api.js 5492d48b4167179fd979fae99f0c21dc2d0f03460be9ff2d35e62225c58c4c9c
F ext/fiddle/testing-common.js 723aada13d90a5ee3f0f8f5b5b88e46954becae5d2b04ded811d90106057f4ac F ext/fiddle/testing-common.js a2527fd8dfb500bad9b434ae2645bb91489792115ee1e1b4b53cac4e9198992a
F ext/fiddle/testing1.html 026502e5d5e6a250e4101f8e8948708a1295ce831a094d741839ecaf788d8533 F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a
F ext/fiddle/testing1.js b9dd06fd02fbcf947794ceb0bcca1a00e3440d80bf1d819a73bbcac25c87086e F ext/fiddle/testing1.html c00236d71b7f7523b722ae2f79cb2b734e6ed4ff16102fa69974145f6e2bfc95
F ext/fiddle/testing1.js a2cee7ee12c2e1756e775125b0f9950dc5e5faeeeb4979c6d9894626d90cb5d9
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
@ -1968,8 +1969,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 70f91fab825d365f505750acdb8d3ae532880c4cdb64d1e61bb21b24a115958b P 526c8c728019b317624a93f6f07840ca524bca84e7c03ce5e86e38953146236f
R aa6cc8d5c8c48a5dcba3187d74399b46 R 8d308c6cc994c31188b8c779dc67218e
U stephan U stephan
Z 0033b446f56e6779d5a11407c4e1444a Z 3a03c1dbb04138d0aaacfe24b6f85ef3
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
526c8c728019b317624a93f6f07840ca524bca84e7c03ce5e86e38953146236f ed19fef3459499abb0a4a010f368b4576d6e068d930c8480446ea677ac87c1c1