Ver Fonte

Update SquiLu examples to work with global table_* functions.

mingodad há 9 anos atrás
pai
commit
11fdba5d9c

+ 5 - 5
SquiLu-ourbiz/companies-uk.nut

@@ -17,8 +17,8 @@ local function sqliteTrace(udata, sql)
 }
 
 local function getCompaniesUkDBFileName(){
-	if (globals.rawget("jniLog", false)) return APP_CODE_FOLDER + "/companies-uk-RG.db";
-	if (globals.rawget("WIN32", false)) return APP_CODE_FOLDER + "/../../companies-uk/companies-uk-RG.db";
+	if (table_rawget(globals, "jniLog", false)) return APP_CODE_FOLDER + "/companies-uk-RG.db";
+	if (table_rawget(globals, "WIN32", false)) return APP_CODE_FOLDER + "/../../companies-uk/companies-uk-RG.db";
 	//return APP_CODE_FOLDER + "/../../companies-uk/companies-uk-RG.db";
 	//return APP_CODE_FOLDER + "/../../companies-uk/companies-uk-RG-2014-07.db";
 	//return APP_CODE_FOLDER + "/../../companies-uk/companies-uk-2013-10.db";
@@ -567,11 +567,11 @@ local function getExtraCompanyDataOnNet(cnum){
 	
 	local data = json2var(page);
 	local tdata = [];
-	local results = data.rawget("results", []);
+	local results = table_rawget(data, "results", []);
 	for(var i=0, len=results.len(); i < len; ++i)
 	{
 		local rec = results[i];
-		local line = rec.rawget("type", "") + "|" + rec.rawget("date", "") + "|" + rec.rawget("description", "");
+		local line = table_rawget(rec, "type", "") + "|" + table_rawget(rec, "date", "") + "|" + table_rawget(rec, "description", "");
 		line = line.gsub("\n", "\\n");
 		tdata.push( line );
 	}
@@ -623,7 +623,7 @@ local my_uri_handlers = {
 			foreach(k in filed_names) data[k] <-request.get_var(query_string, k);
 		}
 		else foreach(k in filed_names) data[k] <- null;
-		if(!data.rawget("page", null))  data.page <- 0;
+		if(!table_rawget(data, "page", null))  data.page <- 0;
 		else data.page = data.page.tointeger();
 
 		local errcode;

+ 3 - 3
SquiLu-ourbiz/db-updater.nut

@@ -140,7 +140,7 @@ class DBTableUpdateBase {
 		httpRequest.send_my_request("POST", url, body_str, body_str.len(),
 					 useMultiPart ? boundary + 2 : 0, //+2 to discount "--"
 					 useSLE);
-		//local check_idle = globals.rawget("Fl", false);
+		//local check_idle = table_rawget(globals, "Fl", false);
 		while( httpRequest.outstanding() )
 		{
 			httpRequest.pump();
@@ -155,7 +155,7 @@ class DBTableUpdateBase {
 			version = 0;
 			local rec = {};
 			appServer.asle2map(my_result, rec);
-			local new_id = rec.rawget("id", 0).tointeger();
+			local new_id = table_rawget(rec, "id", 0).tointeger();
 			if(new_id == 0) throw("Could not update this record !");
 			edit_id = new_id;
 		}
@@ -165,7 +165,7 @@ class DBTableUpdateBase {
 		{
 			local rec = {};
 			appServer.sle2map(my_result, rec);
-			local changes = rec.rawget("changes", 0).tointeger();
+			local changes = table_rawget(rec, "changes", 0).tointeger();
 			if(changes == 0) {
 				if(dbAction == edbAction.e_update) throw("Could not update this record !");
 				else throw("Could not delete this record !");

+ 49 - 47
SquiLu-ourbiz/fluid2SquiLu.nut

@@ -29,6 +29,8 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
+ ::__max_print_stack_str_size <- 256; //to not print long strings in stack trace
+
 // Configuration variables. Change the fields of the configuration table to 
 // match your preferences. You can also override them on the command line.
 local default_configuration = {
@@ -229,12 +231,12 @@ Write.Function <- function(t, ind){
 	t.varname <- "res";
 	Write.group(t.body, ind+1);
 	local names = [];
-	foreach(i in t.body) names.push(i.rawget("varname", null));
+	foreach(i in t.body) names.push(table_rawget(i, "varname", null));
 	if (names.len() > 0) Output(ind+1, "return %s;\n", names.concat(", "));
 	Output(ind, "}\n\n");
 }
 
-function getClassNameOrKey(t){return  t.attr.rawget("class", false) || t.key;}
+function getClassNameOrKey(t){return  table_rawget(t.attr, "class", false) || t.key;}
 
 Write.Atributes <- function(t, ind, name){
 	local lname;
@@ -258,11 +260,11 @@ Write.Atributes <- function(t, ind, name){
 
 Write.widget <- function(t, ind){
 	local fgroup = table_get(Grammar.groups, t.key, false);
-	if (table_get(Grammar.composed, t.key, false) && t.attr.rawget("type", false)){
+	if (table_get(Grammar.composed, t.key, false) && table_rawget(t.attr, "type", false)){
 		t.key = t.key.slice(0,3) + t.attr.type + t.key.slice(2);
 		t.attr.type = null;
 	}
-	local klass = t.attr.rawget("class", false) || t.key;
+	local klass = table_rawget(t.attr, "class", false) || t.key;
 	local constClearLabel = "_ClearLabel";
 	local isClearLabel = false;
 	if (klass.match(constClearLabel)){ 
@@ -273,8 +275,8 @@ Write.widget <- function(t, ind){
 	local ar = t.attr.xywh;
 	local x = ar[0], y = ar[1], w = ar[2], h = ar[3];
 	local name = configuration.currentvar;
-	t.xoffset <- t.parent.rawget("xoffset", "");
-	t.yoffset <- t.parent.rawget("yoffset", "");
+	t.xoffset <- table_rawget(t.parent, "xoffset", "");
+	t.yoffset <- table_rawget(t.parent, "yoffset", "");
 	local newMethodCallStr, firstParamStr;
 	if (klass.match("^fltk%.")) {
 		newMethodCallStr = "";
@@ -288,7 +290,7 @@ Write.widget <- function(t, ind){
 	//print(t.key, name, klass)
 	Output(ind, "{\n"); 
 	++ind;
-	local dirty_name =  t.attr.rawget("dirty_name", false);
+	local dirty_name =  table_rawget(t.attr, "dirty_name", false);
 	local need_var_assign = true;
 	if(dirty_name && dirty_name[0] == '@'){
 		need_var_assign = false;
@@ -296,14 +298,14 @@ Write.widget <- function(t, ind){
 		Output(ind, "local o = %s;\n", dirty_name);
 		Output(ind, "Fl_Group.current()->add(o);\n", dirty_name);
 		Output(ind, "o->resize(%s%s%d, %s%d, %d, %d);\n",  firstParamStr, t.xoffset, x, t.yoffset, y, w, h);
-		if (! isClearLabel && t.attr.rawget("label", false)) 
+		if (! isClearLabel && table_rawget(t.attr, "label", false)) 
 			Output(ind, "o->lable(%s(%q));\n", configuration.textfilter, t.attr.label);
 	}
 	else
 	{
 		Output(ind, "local %s = %s%s(%s%s%d, %s%d, %d, %d", name, 
 			klass, newMethodCallStr, firstParamStr, t.xoffset, x, t.yoffset, y, w, h);
-		if (! isClearLabel && t.attr.rawget("label", false)) Output(0, ", %s(%q)", configuration.textfilter, t.attr.label);
+		if (! isClearLabel && table_rawget(t.attr, "label", false)) Output(0, ", %s(%q)", configuration.textfilter, t.attr.label);
 		Output(0, ");\n");
 	}	
 
@@ -336,19 +338,19 @@ Write.widget <- function(t, ind){
 	
 	Write.Atributes(t, ind, name);
 
-	local img = t.attr.rawget("image", false);
+	local img = table_rawget(t.attr, "image", false);
 	if (img){
 		local ext = img.match("%.(%a+)$").toupper();
 		Output(ind, "%s.image(Fl_%s_Image(%q));\n", name, Grammar.images[ext], img);
 	}
-	if (t.attr.rawget("modal", false)) Output(ind, "%s.set_modal();\n", name);
-	if (t.attr.rawget("non_modal", false)) Output(ind, "%s.set_non_modal();\n", name);
+	if (table_rawget(t.attr, "modal", false)) Output(ind, "%s.set_modal();\n", name);
+	if (table_rawget(t.attr, "non_modal", false)) Output(ind, "%s.set_non_modal();\n", name);
 	for(local i=0; i <= 3; ++i){
-		local code = t.attr.rawget("code" + i, false);
+		local code = table_rawget(t.attr, "code" + i, false);
 		if (code && code.len()) {
 			if(code[0] == '='){
 				code = code.slice(1);
-				local macro = t.attr.rawget("macro_name", false);
+				local macro = table_rawget(t.attr, "macro_name", false);
 				if(macro){
 					macro = macro.split(' ');
 					code = code.gsub("$%((%d+)%)", function(m){
@@ -365,9 +367,9 @@ Write.widget <- function(t, ind){
 			else Output(ind, "%s\n", code);
 		}
 	}
-	if (t.attr.rawget("noborder", false)) Output(ind, "%s.clear_border();\n", name);
-	if (t.attr.rawget("size_range", false)) Output(ind, "%s.size_range(%s);\n", name, table.concat(t.attr.size_range, ", "));
-	local cb = t.attr.rawget("callback", false);
+	if (table_rawget(t.attr, "noborder", false)) Output(ind, "%s.clear_border();\n", name);
+	if (table_rawget(t.attr, "size_range", false)) Output(ind, "%s.size_range(%s);\n", name, table.concat(t.attr.size_range, ", "));
+	local cb = table_rawget(t.attr, "callback", false);
 	if (cb){
 		if (cb.match("^[%w_]+$")) Output(ind, "%s.callback(%s);\n", name, cb);
 		else
@@ -377,7 +379,7 @@ Write.widget <- function(t, ind){
 			Output(ind, "});\n");
 		}
 	}
-	if (t.rawget("body", false) && t.body.len() > 0) {
+	if (table_rawget(t, "body", false) && t.body.len() > 0) {
 		Output(ind, "{\n");
 		Write.group(t.body, ind+1);
 		Output(ind, "}\n");
@@ -387,18 +389,18 @@ Write.widget <- function(t, ind){
 		if (klass.match("^fltk%.")) Output(ind, "%s.end();\n", name);
 		else Output(ind, "%s.end();\n", name);
 	}
-	if (t.attr.rawget("resizable", false)) { 
+	if (table_rawget(t.attr, "resizable", false)) { 
 		if (t.parent.key != "Function"){
 			Output(ind, "Fl_Group.current().resizable(%s);\n", name);
 			t.parent.resized <- true;
 		}
-		else if (! t.rawget("resized", false) ) Output(ind, "%s.resizable(%s);\n", name, name);
+		else if (! table_rawget(t, "resized", false) ) Output(ind, "%s.resizable(%s);\n", name, name);
 	}
 	Output(ind-1, "}\n"); 
 }
 
 Write.Submenu <- function(t, ind){
-	if (t.parent.rawget("path", false)) t.path <- t.parent.path + "/" + t.attr.label;
+	if (table_rawget(t.parent, "path", false)) t.path <- t.parent.path + "/" + t.attr.label;
 	else t.path <- t.attr.label;
 
 	Output(ind, "//%s %s\n", t.name, t.path);
@@ -407,13 +409,13 @@ Write.Submenu <- function(t, ind){
 }
 
 Write.MenuItem <- function(t, ind){
-	if (t.parent.rawget("path", false)) t.path <- t.parent.path + "/" +  t.attr.rawget("label", "");
+	if (table_rawget(t.parent, "path", false)) t.path <- t.parent.path + "/" +table_rawget(t.attr, "label", "");
 	else t.path <- t.attr.label;
 
 	local w = t;
-	while (!w.rawget("varname", false)) w = w.parent;
+	while (!table_rawget(w, "varname", false)) w = w.parent;
 
-	local cb = t.attr.rawget("callback", false);
+	local cb = table_rawget(t.attr, "callback", false);
 	
 	if (!cb && t.name > ""){
 		//cb = "dispatch_func(" + t.name + "_cb, self)";
@@ -426,7 +428,7 @@ Write.MenuItem <- function(t, ind){
 		Output(ind, "o.add(%s(%q)", configuration.textfilter, t.path);
 	}
 
-	Output(0, ", %s", t.attr.rawget("shortcut", "0").tostring());
+	Output(0, ", %s", table_rawget(t.attr, "shortcut", "0").tostring());
 	if (cb){
 		if (cb.match("^[%w_]+$")) Output(ind, ", %s", cb);
 		else
@@ -438,27 +440,27 @@ Write.MenuItem <- function(t, ind){
 
 		if (t.name > ""){
 			local ind2 = 0;
-			if (t.attr.rawget("deactivate", false)) Output2(ind2, "// deactivated base_class.%s\n", t.name);
+			if (table_rawget(t.attr, "deactivate", false)) Output2(ind2, "// deactivated base_class.%s\n", t.name);
 			Output2(ind2, "function base_class.%s_cb(sender);\n", t.name);
 			Output2(ind2+2, "fl_alert(\"%s en construccion\");\n", t.name);
 			Output2(ind2, "}\n\n", t.name);
 		}
 	}
 	local type = null;
-	if(t.attr.rawget("type", false)) type = Grammar.menu_const[t.attr.type];
-	if (t.attr.rawget("divider", false)){ 
+	if(table_rawget(t.attr, "type", false)) type = Grammar.menu_const[t.attr.type];
+	if (table_rawget(t.attr, "divider", false)){ 
 		if (type) type = type + Grammar.menu_const.divider;
 		else type = Grammar.menu_const.divider;
 	}
-	if (t.attr.rawget("deactivate", false)){ 
+	if (table_rawget(t.attr, "deactivate", false)){ 
 		if (type) type = type + Grammar.menu_const.deactivate;
 		else type = Grammar.menu_const.deactivate ;
 	}
-	if (t.attr.rawget("hide", false)) { 
+	if (table_rawget(t.attr, "hide", false)) { 
 		if (type) type = type + Grammar.menu_const.hide;
 		else type = Grammar.menu_const.hide;
 	}
-	if (t.attr.rawget("value", false)){
+	if (table_rawget(t.attr, "value", false)){
 		if (type) type = type + 4; // FL_MENU_VALUE = 4
 		else type = 4; // FL_MENU_VALUE = 4 
 	}
@@ -471,7 +473,7 @@ Write.MenuItem <- function(t, ind){
 
 Write.group <- function(t, ind){
 	foreach(i in t){
-		if (Write.get(i.key, false))	Write[i.key](i, ind);
+		if (table_get(Write, i.key, false))	Write[i.key](i, ind);
 		else if (i.key.match("^Fl_")) Write.widget(i, ind);
 	}
 }
@@ -498,11 +500,11 @@ Write.decl <- function(t, ind){}
 Write.declblock <- function(t, ind){
 	Output(ind, "%s\n", t.name);
 	Write.group(t.body, ind+1);
-	Output(ind, "%s\n", t.attr.rawget("after", "}"));
+	Output(ind, "%s\n", table_rawget(t.attr, "after", "}"));
 }
 
 Write["class"] <- function(t, ind){
-	Output(ind, "class %s extends %s {\n", t.name, t.rawget("class", "?"));
+	Output(ind, "class %s extends %s {\n", t.name, table_rawget(t, "class", "?"));
 	local vars = [];
 	FindMembers.group(t.body, vars);
 	WriteVariables(ind+1, vars, "class members");
@@ -517,9 +519,9 @@ Write.widget_class <- function(t, ind){
 	local vars = [];
 	local wObj = "";
 	local typeName;
-	//if (t.attr.rawget("class", false) && t.attr["class"].match("^Fl_")) typeName = "fltk." + t.attr["class"];
+	//if (table_rawget(t.attr, "class", false) && t.attr["class"].match("^Fl_")) typeName = "fltk." + t.attr["class"];
 	//else  
-	typeName = t.attr.rawget("class", "Fl_Group");
+	typeName = table_rawget(t.attr, "class", "Fl_Group");
 
 	local widgetType = typeName;
 	Output(ind, "class %s extends %s {\n", t.name, widgetType);
@@ -530,13 +532,13 @@ Write.widget_class <- function(t, ind){
 	
 	local ar = t.attr.xywh;
 	local x = ar[0], y = ar[1], w = ar[2], h = ar[3];
-	local wlabel = t.attr.rawget("label", false);
+	local wlabel = table_rawget(t.attr, "label", false);
 
 	local strCreateWidget = format("constructor(px=%d, py=%d, pw=%d, ph=%d, pl=%s){\n",
 		x, y, w, h, wlabel ? format( "%s(%q)", configuration.textfilter, wlabel): "null");
 	Output(ind+1, strCreateWidget);
 	Output(ind+2, "base.constructor(px, py, pw, ph, pl);\n");
-	if (! (t.attr.rawget("class", "")).match("_Window$") ) {
+	if (! (table_rawget(t.attr, "class", "")).match("_Window$") ) {
 		t.xoffset <- "_x + ";
 		t.yoffset <- "_y + ";
 		Output(ind+2, "local _x = %d, _y = %d;\n", x, y);
@@ -554,7 +556,7 @@ Write.widget_class <- function(t, ind){
 
 FindMembers.group <- function(t, list){
 	foreach(i in t){
-		if (FindMembers.get(i.key, false)) FindMembers[i.key](i, list);
+		if (table_get(FindMembers, i.key, false)) FindMembers[i.key](i, list);
 		else if (i.key.match("^Fl_")) FindMembers.widget(i, list);
 		else if (i.key == "Submenu") FindMembers.submenu(i, list);
 		else if (i.key == "MenuItem") FindMembers.menuitem(i, list);
@@ -562,14 +564,14 @@ FindMembers.group <- function(t, list){
 }
 
 function GetAccessMode(t, mode){
-	foreach(i in ["private", "protected", "public"]) if (t.attr.get(i, false)) mode = i;
+	foreach(i in ["private", "protected", "public"]) if (table_get(t.attr, i, false)) mode = i;
 	return mode;
 }
 
 FindMembers.Function <- function(t, list){
 	if (t.name == "") t.name = "make_window()";
 	local name = t.name.match("[%w_]+");
-	if (t.parent.rawget("name",  "") == name) { // constructor
+	if (table_rawget(t.parent, "name",  "") == name) { // constructor
 		t.name = "__constructor()";
 		t.parent.constructor <- t.name;
 	}
@@ -578,7 +580,7 @@ FindMembers.Function <- function(t, list){
 }
 
 FindMembers["class"] <- function(t, list){
-	list.push([ t.name, "public", t.attr.rawget("class", "null") ]);
+	list.push([ t.name, "public", table_rawget(t.attr, "class", "null") ]);
 }
 
 FindMembers.widget_class <- FindMembers["class"];
@@ -590,7 +592,7 @@ FindMembers.widget <- function(t, list){
 	else if (t.name.match("^[%w_]+$")){
 		list.push([ t.name, GetAccessMode(t, "public") , getClassNameOrKey(t)]);
 	}
-	if (t.rawget("body", false)) FindMembers.group(t.body, list);
+	if (table_rawget(t, "body", false)) FindMembers.group(t.body, list);
 }
 
 FindMembers.decl <- function(t, list){
@@ -605,7 +607,7 @@ FindMembers.submenu <- function(t, list){
 				//print("body", k2, v2);
 				if(type(v2) == "table"){
 					//foreach(k3,v3 in v2) print("menuitem", k3,v3);
-					if(v2.rawget("name", false)) list.push([ v2.name, GetAccessMode(t, "public"), getClassNameOrKey(t)]);
+					if(table_rawget(v2, "name", false)) list.push([ v2.name, GetAccessMode(t, "public"), getClassNameOrKey(t)]);
 				}
 			}
 		}
@@ -621,7 +623,7 @@ function WriteVariables(ind, vars, type){
 	local defined = {};
 	Output(ind, "// Declaration of %s\n", type);
 	foreach(i in vars) {
-		if (defined.get(i[0], false)) {}
+		if (table_get(defined, i[0], false)) {}
 		else if (i[1] == "public") {
 			local type_name = i.get(2, false);
 			if(type_name) Output(ind, "%s : %s;\n", i[0], type_name);
@@ -707,7 +709,7 @@ function Fluid2SquiLu(infile, outfile, config){
 		fd.close();
 	}
 	if (outdata2.len() > 0){
-		local fd = file("extra-generated-code.lua", "wt");
+		local fd = file("extra-generated-code.nut", "wt");
 		fd.write(outdata2);
 		fd.close();
 	}
@@ -735,7 +737,7 @@ function mydebughook(event_type,sourcefile,line,funcname)
 			else print(k,v);
 		}
 */
-		local pos = getstackinfos(2).locals.rawget("pos", false);
+		local pos = table_rawget(getstackinfos(2).locals, "pos", false);
 		if(isDebugging || pos == 8926) {
 			foreach(k,v in getstackinfos(2)) {
 				if(type(v) == "table") {

+ 2 - 1
SquiLu-ourbiz/gui2nut

@@ -1,2 +1,3 @@
 #!/bin/sh
-squilu fluid2SquiLu.nut ourbiz-gui.fl ourbiz-gui.nut
+base=${1:-"ourbiz"}
+squilu fluid2SquiLu.nut ${base}-gui.fl ${base}-gui.nut

+ 1 - 1
SquiLu-ourbiz/happyhttp.nut

@@ -197,7 +197,7 @@ class HappyHttpConnection {
 		// check headers for content-length
 		// TODO: check for "Host" and "Accept-Encoding" too
 		// and avoid adding them ourselves in putrequest()
-		if( headers ) gotcontentlength = headers.rawget("content-length", false);
+		if( headers ) gotcontentlength = table_rawget(headers, "content-length", false);
 
 		putrequest( method, url );
 

+ 21 - 21
SquiLu-ourbiz/index.tpl

@@ -112,7 +112,7 @@ local company_ages = [
 ]
 
 function emptyOnNull(key){
-	local str = this.get(key, null);
+	local str = table_get(this, key, null);
 	return str ? str : "";
 }
 
@@ -128,7 +128,7 @@ function getPageLink(page){
 }
 
 ?>
-<? if (this.get("use_vjs", false) && this.get("page_name", null) == "search_results"){ ?>
+<? if (table_get(this, "use_vjs", false) && table_get(this, "page_name", null) == "search_results"){ ?>
     <!--Load the AJAX API-->
     <script type="text/javascript" src="https://www.google.com/jsapi"></script>
     <script type="text/javascript">
@@ -378,7 +378,7 @@ function sortTable(id, col, numeric){
 	<button type=reset>Reset</button></td></tr>
 </table>
 </form>
-<? if (this.get("isAndroid", false)){ ?>
+<? if (table_get(this, "isAndroid", false)){ ?>
 <a href="/mk-gpx?sic_term=<?=search_sic_code?>">Generate GPX</a>
 <? } ?>
         <div class="horSeparator"></div>
@@ -386,13 +386,13 @@ function sortTable(id, col, numeric){
     <article class="post">
 <? if (page_name == "search_results"){ ?>
 
-<? if (this.get("sic_street", null)  == "street"){ ?>
+<? if (table_get(this, "sic_street", null)  == "street"){ ?>
 
 <header><h1 id="results">Street Search Results</h1></header>
-<? if (this.get("queryWasInterrupted", false)){ ?>
+<? if (table_get(this, "queryWasInterrupted", false)){ ?>
 <p>Please try again with more specific search terms, it took too long !</p>
 <? } ?>
-<? if (this.get("rows", false) && rows.len() > 0){ ?>
+<? if (table_get(this, "rows", false) && rows.len() > 0){ ?>
 <table class="nj">
 <tr><th>Company</th><th>Street</th><th>Postcode</th></tr>
 <? foreach( k, rec in rows){ ?>
@@ -406,29 +406,29 @@ function sortTable(id, col, numeric){
 <tr><td>
 &nbsp;
 <? if (hasPrevious){ ?>
-<a href="/?search_sic_code=<?=url_encode(this.get("search_sic_code", ""))?>&page=<?=page-1?>">Prev</a>
+<a href="/?search_sic_code=<?=url_encode(table_get(this, "search_sic_code", ""))?>&page=<?=page-1?>">Prev</a>
 <? } ?>
 </td><td>&nbsp;</td><td>
 &nbsp;
 <? if (hasNext){ ?>
-<a href="/?search_sic_code=<?=url_encode(this.get("search_sic_code", ""))?>&page=<?=page+1?>">Next</a>
+<a href="/?search_sic_code=<?=url_encode(table_get(this, "search_sic_code", ""))?>&page=<?=page+1?>">Next</a>
 <? } ?>
 </td></tr>
 <? } ?>
 </table>
 <?
 	}
-} else if (this.get("sicSearchResults", false)) { ?>
+} else if (table_get(this, "sicSearchResults", false)) { ?>
 
 <header><h1 id="results">SIC Code Search Results</h1></header>
-<? if (this.get("queryWasInterrupted", false)){ ?>
+<? if (table_get(this, "queryWasInterrupted", false)){ ?>
 <p>Please try again with more specific search terms, it took too long !</p>
 <? } ?>
-<? if (this.get("rows", false) && rows.len() > 0){ ?>
+<? if (table_get(this, "rows", false) && rows.len() > 0){ ?>
 <table class="nj">
 <tr><th>Code</th><th>Description</th><th>#Companies</th></tr>
 <? foreach( k, rec in rows){ ?>
-<tr><td><a href="#search_form" onclick="setSicCode(<?=rec[1]?>)"><?=rec[1]?></a></td><td><?=rec[2]?></td><td><?=rec.get(3, "")?></td></tr>
+<tr><td><a href="#search_form" onclick="setSicCode(<?=rec[1]?>)"><?=rec[1]?></a></td><td><?=rec[2]?></td><td><?=table_get(rec, 3, "")?></td></tr>
 <? }
 	page = page.tointeger();
 	local hasPrevious = page > 0;
@@ -438,12 +438,12 @@ function sortTable(id, col, numeric){
 <tr><td>
 &nbsp;
 <? if (hasPrevious){ ?>
-<a href="/?search_sic_code=<?=url_encode(this.get("search_sic_code", ""))?>&page=<?=page-1?>">Prev</a>
+<a href="/?search_sic_code=<?=url_encode(table_get(this, "search_sic_code", ""))?>&page=<?=page-1?>">Prev</a>
 <? } ?>
 </td><td>&nbsp;</td><td>
 &nbsp;
 <? if (hasNext){ ?>
-<a href="/?search_sic_code=<?=url_encode(this.get("search_sic_code", ""))?>&page=<?=page+1?>">Next</a>
+<a href="/?search_sic_code=<?=url_encode(table_get(this, "search_sic_code", ""))?>&page=<?=page+1?>">Next</a>
 <? } ?>
 </td></tr>
 <? } ?>
@@ -453,16 +453,16 @@ function sortTable(id, col, numeric){
 } else { ?>
 
 <header><h1 id="results">Companies Search Results</h1></header>
-<? if (this.get("queryWasInterrupted",false)) { ?>
+<? if (table_get(this, "queryWasInterrupted",false)) { ?>
 <p>Please try again with more specific search terms, it took too long !</p>
 <? } ?>
-<? if (this.get("rows", false) && rows.len() > 0){ ?>
+<? if (table_get(this, "rows", false) && rows.len() > 0){ ?>
 <table class="nj dist" id="tblcr">
 <tr><th><a href="" onclick="return sortTable('tblcr', 0)">Number</a></th>
 	<th><a href="" onclick="return sortTable('tblcr', 1)">Name</a></th>
 	<th><a href="" onclick="return sortTable('tblcr', 2, true)">Age (years)</a></th>
 <?
-	local hasDistances = this.get("search_origin_post_code", false) && search_origin_post_code.len() > 0;
+	local hasDistances = table_get(this, "search_origin_post_code", false) && search_origin_post_code.len() > 0;
 	if (hasDistances){
 ?>
 <th><a href="" onclick="return sortTable('tblcr', 3, true)">Dist. Meters</a></th>
@@ -503,7 +503,7 @@ function sortTable(id, col, numeric){
  }
    else if (page_name == "view_company") {
        var pc1, pc2, google_map_url, geo_link;
-       if (company.get("post_code", false) && company.post_code.len() > 0){
+       if (table_get(company, "post_code", false) && company.post_code.len() > 0){
     	company.post_code.gmatch("([^%s]+)%s([^%s]+)", function(m1,m2) {pc1=m1;pc2=m2;});
         if (pc1 && pc2){
           google_map_url = format("https://maps.google.co.uk/maps?key=AIzaSyAF53Wut8B5cvaUDY-pfegBhbftJdRHovM&amp;channel=cs&amp;q=%s+%s&amp;ie=UTF8&amp;hq=&amp;hnear=%s+%s,+United+Kingdom&amp;gl=uk&amp;t=m&amp;z=14&amp;source=embed", pc1, pc2, pc1, pc2);
@@ -541,7 +541,7 @@ function sortTable(id, col, numeric){
 <? } ?>
 </td></tr>
 <tr><td>Previous Names</td><td>
-<? if (this.get("company_old_names", false) && company_old_names.len() > 0){ ?>
+<? if (table_get(this, "company_old_names", false) && company_old_names.len() > 0){ ?>
 <table>
 <tr><th>Date of change</th><th>Previous Name</th></tr>
 <? foreach(k,v in company_old_names){ ?>
@@ -551,7 +551,7 @@ function sortTable(id, col, numeric){
 <? } ?>
 </td></tr>
 </table>
-<? if (this.get("extra_data", false) && extra_data.len() > 0){ ?>
+<? if (table_get(this, "extra_data", false) && extra_data.len() > 0){ ?>
         <table>
           <tr><th>Doc. Type</th><th>Date</th><th>Description</th></tr>
           <? foreach(k,v in extra_data.split('\n')){
@@ -563,7 +563,7 @@ function sortTable(id, col, numeric){
 <? } ?>
 
 <?
-  if (this.get("show_map", false) && pc1 && pc2){
+  if (table_get(this, "show_map", false) && pc1 && pc2){
 ?>
 <br />
 <div style="margin: 0 auto; width: 425px;">

+ 113 - 112
SquiLu-ourbiz/ourbiz.nut

@@ -188,13 +188,13 @@ const C_zip = "zip";
 // generated-code:end 
 
 local globals = getroottable();
-if(!globals.rawget("APP_CODE_FOLDER", false)) ::APP_CODE_FOLDER <- ".";
+if(!table_rawget(globals, "APP_CODE_FOLDER", false)) ::APP_CODE_FOLDER <- ".";
 
 local constants = getconsttable();
 
 local function getOurbizDBFileName(){
-	if(globals.rawget("jniLog", false)) return APP_CODE_FOLDER + "/ourbiz.db";
-	if(globals.rawget("WIN32", false)) return APP_CODE_FOLDER + "/../../ourbiz-uk/ourbiz.db";
+	if(table_rawget(globals, "jniLog", false)) return APP_CODE_FOLDER + "/ourbiz.db";
+	if(table_rawget(globals, "WIN32", false)) return APP_CODE_FOLDER + "/../../ourbiz-uk/ourbiz.db";
 	return "/home/mingo/dev/FrontAccountLua/ourbiz.db";
 	//return "file:ourbiz_db?mode=memory&cache=shared";
 }
@@ -206,7 +206,7 @@ local function getOurbizDB(){
 	//return checkCachedDB(APP_CODE_FOLDER + "/ourbiz.db");
 }
 
-if(globals.rawget("AT_DEV_DBG", false) || !globals.rawget("PdfSqlTable", false)) {
+if(table_rawget(globals, "AT_DEV_DBG", false) || !table_rawget(globals, "PdfSqlTable", false)) {
 	dofile(APP_CODE_FOLDER + "/pdf-table.nut");
 }
 
@@ -225,7 +225,7 @@ local function escape_sql_like_search_str(str){
 local function mkEmptyWhenZero(tbl, key){
 	if (type(key) == "array"){
 		foreach( v in key ) {
-			if (tbl.rawget(v, false) == "0") tbl[v] = "";
+			if (table_rawget(tbl, v, false) == "0") tbl[v] = "";
 		}
 	}
 	else if (tbl[key] == 0) tbl[key] = "";
@@ -240,8 +240,9 @@ local function mytofloat(val){
 	}
 }
 
+local sqlNull = SQLite3.Null;
 local function add2sle(out_result, str){
-	str = str.tostring();
+	str = (str ==  sqlNull ? "" : str).tostring();
 	out_result.write(get_sle_size(str.len()), str);
 }
 
@@ -285,7 +286,7 @@ local function mkSLEArray(str_buf, header, body, doAppend=false)
 	str_buf.write("]]");
 }
 
-constants.rawdelete("TimePeriode");
+table_rawdelete(constants, "TimePeriode");
 
 //enum do not does autonumber with initialization
 enum TimePeriode {is_years = 1, is_months = 2, is_weeks = 3, is_days = 4};
@@ -326,17 +327,17 @@ local function get_sql_bar_chart_statistics_periodes (periode_count, periode_typ
 
 local function getOptionsFromMap(map){
 	local opt = {};
-	opt.search_str <- map.rawget("search_str", null);
-	opt.select_fields <- map.rawget("select_fields", null);
-	opt.search_on <- map.rawget("search_on", "1"); //to make easy on html interface
+	opt.search_str <- table_rawget(map, "search_str", null);
+	opt.select_fields <- table_rawget(map, "select_fields", null);
+	opt.search_on <- table_rawget(map, "search_on", "1"); //to make easy on html interface
 	local CHECK_BOOL = function(field){
-		local value = map.rawget(field, null);
+		local value = table_rawget(map, field, null);
 		if (value == "1") opt[field] <- "1";
 		else opt[field] <- null;
 	}
 	local math_floor = math.floor;
 	local CHECK_INT_DFLT = function(field, dflt) {
-		local value = map.rawget(field, dflt).tointeger();
+		local value = table_rawget(map, field, dflt).tointeger();
 		opt[field] <- math_floor(value);
 	}
 	local CHECK_INT = function(field) {CHECK_INT_DFLT(field, 0) };
@@ -378,7 +379,7 @@ local function getOptionsFromMap(map){
 }
 
 local function checkQueryStringSAB(qs_tbl, so=null){
-	local qs_sab = qs_tbl.rawget("sab", null);
+	local qs_sab = table_rawget(qs_tbl, "sab", null);
 	if (qs_sab){
 		if (qs_sab == "S") {
 			if(so) so.sales <- true;
@@ -396,7 +397,7 @@ local function checkQueryStringSAB(qs_tbl, so=null){
 local function get_search_options(req){
 	local search_opt = getOptionsFromMap(req);
 
-	local sab = req.rawget("sab", null);
+	local sab = table_rawget(req, "sab", null);
 	if (sab && sab.len() > 0){
 		local c = sab.toupper();
 		if (c == "S") search_opt.sales <- true;
@@ -432,7 +433,7 @@ local DB_Manager = class {
 		mf.write("insert into ", table_name,  "(")
 		local isFirst = true;
 		foreach( k,v in editable_fields) {
-			if (data.rawget(v, false)){
+			if (table_rawget(data, v, false)){
 				if (isFirst) isFirst = false;
 				else mf.write(",");
 				mf.write(v);
@@ -441,7 +442,7 @@ local DB_Manager = class {
 		mf.write(") values(");
 		isFirst = true;
 		foreach( k,v in editable_fields) {
-			if (data.rawget(v, false)){
+			if (table_rawget(data, v, false)){
 				if (isFirst) isFirst = false;
 				else mf.write(",");
 				mf.write("?");
@@ -452,7 +453,7 @@ local DB_Manager = class {
 		local stmt = db.prepare(mf.tostring());
 		local x = 0;
 		foreach( k,v in editable_fields) {
-			if (data.rawget(v, false)){
+			if (table_rawget(data, v, false)){
 				stmt.bind_empty_null(++x, data[v]);
 			}
 		}
@@ -467,7 +468,7 @@ local DB_Manager = class {
 		mf.write("update ", table_name,  " set ");
 		local isFirst = true;
 		foreach( k,v in editable_fields) {
-			if (data.rawget(v, false)){
+			if (table_rawget(data, v, false)){
 				if (isFirst) isFirst = false;
 				else mf.write(",");
 				mf.write(v, "=?");
@@ -481,7 +482,7 @@ local DB_Manager = class {
 		local stmt = db.prepare(mf.tostring());
 		local x = 0;
 		foreach( k,v in editable_fields) {
-			if (data.rawget(v, false)) {
+			if (table_rawget(data, v, false)) {
 				stmt.bind_empty_null(++x, data[v]);
 			}
 		}
@@ -550,13 +551,13 @@ WHERE entity_id = ]==], aId);
 	}
 	
 	function save_image (db, tbl_qs, tbl, sle_buf)                                                                              {
-		local thumbnail = tbl_qs.rawget("thumbnail", null);
+		local thumbnail = table_rawget(tbl_qs, "thumbnail", null);
 		if(thumbnail)
 		{
-			local image = tbl_qs.rawget("image", null);
-			local mime_type = tbl_qs.rawget("mime_type", null);
+			local image = table_rawget(tbl_qs, "image", null);
+			local mime_type = table_rawget(tbl_qs, "mime_type", null);
 
-			local id = tbl_qs.rawget("__id__", 0);
+			local id = table_rawget(tbl_qs, "__id__", 0);
 			local isNewRecord = id == 0;
 			if(isNewRecord) id = db.last_row_id(); //new record get last_insert_rowid
 			if(id)
@@ -1533,30 +1534,30 @@ local DB_Entities = class extends DB_Manager {
 
 	function sql_list(qs_tbl, post_tbl){
 		local entity_id;
-		if (qs_tbl.rawget("search", false)) return entities_sql_search_list(qs_tbl, post_tbl);
-		else if (qs_tbl.rawget("past_products", false)) return entity_past_products_get_sql(qs_tbl.past_products.tointeger());
-		else if ( (entity_id = qs_tbl.rawget("history", 0)) ){
-			local htype = qs_tbl.rawget("htype", 0);
+		if (table_rawget(qs_tbl, "search", false)) return entities_sql_search_list(qs_tbl, post_tbl);
+		else if (table_rawget(qs_tbl, "past_products", false)) return entity_past_products_get_sql(qs_tbl.past_products.tointeger());
+		else if ( (entity_id = table_rawget(qs_tbl, "history", 0)) ){
+			local htype = table_rawget(qs_tbl, "htype", 0);
 			local query_limit = table_get(qs_tbl, C_query_limit, 50).tointeger();
 			return entity_sales_history_sql(htype, query_limit, entity_id.tointeger());
 		}
-		else if ( (entity_id = qs_tbl.rawget("statistics", 0)) ){
+		else if ( (entity_id = table_rawget(qs_tbl, "statistics", 0)) ){
 			local periode_count = table_get(qs_tbl, C_periode_count, 12).tointeger();
 			local periode_type = getStatisticsPeriodeType(table_get(qs_tbl, C_periode_type, C_months));
-			local sab = qs_tbl.rawget("sab", "S");
+			local sab = table_rawget(qs_tbl, "sab", "S");
 			return entity_bar_chart_statistics_sql(entity_id.tointeger(), sab, periode_count, periode_type);
 		}
-		else if (qs_tbl.rawget("print_list", false)){
+		else if (table_rawget(qs_tbl, "print_list", false)){
 			return entities_list_sql();
 		}
-		else if (qs_tbl.rawget("pdf", false)){
+		else if (table_rawget(qs_tbl, "pdf", false)){
 			qs_tbl._doc_pdf_ <- get_pdf_list(qs_tbl);
 			return true;
 		}
 	}
 	
 	function get_pdf_list(tbl_qs){
-		local clipped = tbl_qs.rawget("clipped", false);
+		local clipped = table_rawget(tbl_qs, "clipped", false);
 		local pdf = new PdfSqlTable();
 		pdf.page_title = "Entities List";
 		pdf.water_mark = "T H I S   I S   A   D E M O";
@@ -1576,18 +1577,18 @@ order by name
 
 	function sql_get_one(tbl_qs){
 		local id = table_get(tbl_qs, table_name, 0).tointeger();
-		if(tbl_qs.rawget("for_order", false))
+		if(table_rawget(tbl_qs, "for_order", false))
 		{
 			return entity_for_order_get_one(id);
 		}
-		else if(tbl_qs.rawget("with_thumbnail", false))
+		else if(table_rawget(tbl_qs, "with_thumbnail", false))
 		{
 			return [==[
 select e.*, i.thumbnail, i.mime_type
 from entities e left join images i
 on e.image_id = i.id where e.id=?]==];
 		}
-		else if(tbl_qs.rawget("pdf", false))
+		else if(table_rawget(tbl_qs, "pdf", false))
 		{
 			return get_pdf_list(tbl_qs);
 		}
@@ -1610,7 +1611,7 @@ db_ourbiz_tables.entities <- new DB_Entities();
 // orders
 //
 
-if(globals.rawget("AT_DEV_DBG", false) || !globals.rawget("PDF_Order", false)) {
+if(table_rawget(globals, "AT_DEV_DBG", false) || !table_rawget(globals, "PDF_Order", false)) {
 	dofile(APP_CODE_FOLDER + "/pdf-order.nut");
 }
 
@@ -2141,32 +2142,32 @@ local DB_Orders = class extends DB_Manager {
 
 	function sql_list(qs_tbl, post_tbl){
 		local order_id
-		if (qs_tbl.rawget("search", false)) return orders_sql_search_list(qs_tbl, post_tbl);
-		else if( (order_id = qs_tbl.rawget("lines", 0)) )
+		if (table_rawget(qs_tbl, "search", false)) return orders_sql_search_list(qs_tbl, post_tbl);
+		else if( (order_id = table_rawget(qs_tbl, "lines", 0)) )
 		{
 			return orders_lines_sql_for_order(order_id.tointeger());
 		}
-		else if(  (order_id = qs_tbl.rawget("sum", 0)) )
+		else if(  (order_id = table_rawget(qs_tbl, "sum", 0)) )
 		{
-			local query_limit = qs_tbl.rawget("query_limit", 50);
+			local query_limit = table_rawget(qs_tbl, "query_limit", 50);
 			return orders_sum_search_sql(query_limit);
 		}
-		else if( (order_id = qs_tbl.rawget("lines_onhand", 0)) )
+		else if( (order_id = table_rawget(qs_tbl, "lines_onhand", 0)) )
 		{
 			return order_lines_onhand_get_sql(order_id.tointeger());
 		}		
-		else if (qs_tbl.rawget("history", false)){
-			local htype = qs_tbl.rawget("htype", 0).tointeger();
-			local query_limit = qs_tbl.rawget("query_limit", 50).tointeger();
+		else if (table_rawget(qs_tbl, "history", false)){
+			local htype = table_rawget(qs_tbl, "htype", 0).tointeger();
+			local query_limit = table_rawget(qs_tbl, "query_limit", 50).tointeger();
 			return orders_sales_history_sql(htype, query_limit, qs_tbl.history);
 		}
-		else if (qs_tbl.rawget("statistics", false)){
+		else if (table_rawget(qs_tbl, "statistics", false)){
 			local periode_count = table_get(qs_tbl, C_periode_count, 12).tointeger();
-			local periode_type = getStatisticsPeriodeType(qs_tbl.rawget("periode_type", "months"));
-			local sab = qs_tbl.rawget("sab", "S");
+			local periode_type = getStatisticsPeriodeType(table_rawget(qs_tbl, "periode_type", "months"));
+			local sab = table_rawget(qs_tbl, "sab", "S");
 			return sql_bar_chart_statistics(sab, periode_count, periode_type);
 		}
-		else if (qs_tbl.rawget("print_list", false)){
+		else if (table_rawget(qs_tbl, "print_list", false)){
 		}
 	}
 
@@ -2174,7 +2175,7 @@ local DB_Orders = class extends DB_Manager {
 	{
 		_calc_line.reset();
 		local product_id = table_get(tbl_qs, C_product_id, 0).tointeger();
-		local order_id = tbl_qs.rawget("__id__", 0).tointeger();
+		local order_id = table_rawget(tbl_qs, "__id__", 0).tointeger();
 		if(product_id  && order_id)
 		{
 			local db = getOurbizDB();
@@ -2264,11 +2265,11 @@ where o.id = %d and p.id = %d]==], order_id, product_id));
 	
 	function sql_get_one(tbl_qs) {
 		local id = table_get(tbl_qs, table_name, 0).tointeger();
-		if(tbl_qs.rawget("line", false))
+		if(table_rawget(tbl_qs, "line", false))
 		{
 			orders_lines_get_one(id);
 		}
-		else if(tbl_qs.rawget("line_calculated", false))
+		else if(table_rawget(tbl_qs, "line_calculated", false))
 		{
 			local db = getOurbizDB();
 			local stmt = db.prepare(orders_lines_get_one(id));
@@ -2279,7 +2280,7 @@ where o.id = %d and p.id = %d]==], order_id, product_id));
 			calc_order_line(tbl_qs, buf, rec_map);
 			return buf;
 		}
-		else if (tbl_qs.rawget("with_lines", false)){
+		else if (table_rawget(tbl_qs, "with_lines", false)){
 			local db = getOurbizDB();
 			local buf = blob(0, 8192);
 
@@ -2306,7 +2307,7 @@ where o.id = %d and p.id = %d]==], order_id, product_id));
 			buf.write(stmt.asSleArray());
 			return buf;
 		}
-		else if (tbl_qs.rawget("pdf", false)){
+		else if (table_rawget(tbl_qs, "pdf", false)){
 			local db = getOurbizDB();
 			local calc_order = new MyCalcOrderTotals(db);
 			tbl_qs._doc_pdf_ <- calc_order.getPdfOrder(id, "en");
@@ -2317,7 +2318,7 @@ where o.id = %d and p.id = %d]==], order_id, product_id));
 
 	function db_action(db, data){
 		//dbg_dump_map(p.post_map);
-		local action = data.rawget("__action__", false);
+		local action = table_rawget(data, "__action__", false);
 
 		if(action == "calc_line" || action == "calc_order_line")
 		{
@@ -2327,8 +2328,8 @@ where o.id = %d and p.id = %d]==], order_id, product_id));
 		}
 		else if(action == "order_numbering")
 		{
-			local order_id = data.rawget("__id__", 0).tointeger();
-			local version = data.rawget("__version__", 0).tointeger();
+			local order_id = table_rawget(data, "__id__", 0).tointeger();
+			local version = table_rawget(data, "__version__", 0).tointeger();
 			db.exec_dml("begin;");
 			try {
 				if(db.exec_dml(orders_numbering_get_sql(order_id, version)) < 1)
@@ -2359,9 +2360,9 @@ where o.id = %d and p.id = %d]==], order_id, product_id));
 
 	function do_db_action2(db, data){
 /*
-		local action = data.rawget("__action__", false);
-		local order_id = data.rawget("__id__", 0).tointeger();
-		local version = data.rawget("__version__", 0).tointeger();
+		local action = table_rawget(data, "__action__", false);
+		local order_id = table_rawget(data, "__id__", 0).tointeger();
+		local version = table_rawget(data, "__version__", 0).tointeger();
 		
 		local line_id, entity_id, order_type_id, changes = 0;
 		map_str_t order_type_map;
@@ -2591,14 +2592,14 @@ local Product_Calc_price = class
 	function calc_over_sales_by_map(map)
 	{
 		//dbg_dump_map(map);
-		local trigger = map.get(C_trigger, null);
+		local trigger = table_get(map, C_trigger, null);
 		local result_fields = [C_sell_markup, C_discount_over_sales, C_sell_price2];
-		foreach(k in result_fields) this[k] = map.get(k, 0.0);
-		buy_price = map.get(C_buy_price, 0.0);
-		buy_discount = map.get(C_buy_discount, 0.0);
-		buy_other_costs = map.get(C_buy_other_costs, 0.0);
-		sell_price = map.get(C_sell_price, 0.0);
-		price_decimals = map.get(C_price_decimals, 2).tointeger();
+		foreach(k in result_fields) this[k] = table_get(map, k, 0.0);
+		buy_price = table_get(map, C_buy_price, 0.0);
+		buy_discount = table_get(map, C_buy_discount, 0.0);
+		buy_other_costs = table_get(map, C_buy_other_costs, 0.0);
+		sell_price = table_get(map, C_sell_price, 0.0);
+		price_decimals = table_get(map, C_price_decimals, 2).tointeger();
 		
 		local result = calc_over_sales(trigger);
 		foreach(k in result_fields) map[k] = this[k];
@@ -2609,18 +2610,18 @@ local Product_Calc_price = class
 	function calc_by_map(map)
 	{
 		//dbg_dump_map(map);
-		local trigger = map.get(C_trigger, C_sell_markup);
+		local trigger = table_get(map, C_trigger, C_sell_markup);
 		local result_fields = [C_buy_price, C_buy_discount, C_buy_other_costs, C_sell_price, 
 						C_sell_price2, C_sell_markup, C_markup_to_discount];
-		price_decimals = map.get(C_price_decimals, 2).tointeger();
+		price_decimals = table_get(map, C_price_decimals, 2).tointeger();
 		
 		foreach(k in result_fields) {
-			//debug_print("\n", k, ":", map.get(k, 0.0));
-			this[k] = mytofloat(map.get(k, 0.0));
+			//debug_print("\n", k, ":", table_get(map, k, 0.0));
+			this[k] = mytofloat(table_get(map, k, 0.0));
 		}
 		local result = calc(trigger);
 		foreach(k in result_fields) {
-			//debug_print("\n", k, ":", this[k], ":", map.get(k, 0));
+			//debug_print("\n", k, ":", this[k], ":", table_get(map, k, 0));
 			map[k] <- this[k];
 		}
 		
@@ -2720,13 +2721,13 @@ local DB_Products = class extends DB_Manager {
 		_calc_prices = Product_Calc_price();
 	}
 
-	function db_action(db, data){
+	/*function db_action(db, data){
 		mkEmptyWhenZero(data, [C_warranty_id,  C_group_id,  C_measure_unit_id,  C_image_id,  C_sales_tax_id]);
 		return base.db_action(db, data);
-	}
+	}*/
 	
 	function getPdfList(qs_tbl){
-		local clipped = qs_tbl.rawget("clipped", false);
+		local clipped = table_rawget(qs_tbl, "clipped", false);
 		local pdf = new PdfSqlTable();
 		pdf.page_title = "Products List";
 		pdf.water_mark = "T H I S   I S   A   D E M O";
@@ -2741,26 +2742,26 @@ local DB_Products = class extends DB_Manager {
 
 	function sql_list(qs_tbl, post_tbl){
 		local product_id;
-		if (qs_tbl.rawget("search", false)) return products_sql_search_list(qs_tbl, post_tbl);
-		else if ( (product_id = qs_tbl.rawget("prices_by_quantity", 0)) ) return product_prices_list_sql(product_id.tointeger());
-		else if ( (product_id = qs_tbl.rawget("past_products", 0)) ) return entity_past_products_get_sql(product_id.tointeger());
-		else if ( (product_id = qs_tbl.rawget("last_order_lines", 0)) ) return last_product_order_lines_get_sql(product_id.tointeger());
-		else if ( (product_id = qs_tbl.rawget("appear_together", 0)) ) return product_appear_together_get_sql(product_id.tointeger());
-		else if ( (product_id = qs_tbl.rawget("history", 0)) ) {
-			local htype = qs_tbl.rawget("htype", 0).tointeger();
-			local query_limit = qs_tbl.get(C_query_limit, 50).tointeger();
+		if (table_rawget(qs_tbl, "search", false)) return products_sql_search_list(qs_tbl, post_tbl);
+		else if ( (product_id = table_rawget(qs_tbl, "prices_by_quantity", 0)) ) return product_prices_list_sql(product_id.tointeger());
+		else if ( (product_id = table_rawget(qs_tbl, "past_products", 0)) ) return entity_past_products_get_sql(product_id.tointeger());
+		else if ( (product_id = table_rawget(qs_tbl, "last_order_lines", 0)) ) return last_product_order_lines_get_sql(product_id.tointeger());
+		else if ( (product_id = table_rawget(qs_tbl, "appear_together", 0)) ) return product_appear_together_get_sql(product_id.tointeger());
+		else if ( (product_id = table_rawget(qs_tbl, "history", 0)) ) {
+			local htype = table_rawget(qs_tbl, "htype", 0).tointeger();
+			local query_limit = table_get(qs_tbl, C_query_limit, 50).tointeger();
 			return product_sales_history_sql(htype, query_limit, product_id.tointeger());
 		}
-		else if ( (product_id = qs_tbl.rawget("statistics", 0)) ){
-			local periode_count = qs_tbl.get(C_periode_count, 12).tointeger();
+		else if ( (product_id = table_rawget(qs_tbl, "statistics", 0)) ){
+			local periode_count = table_get(qs_tbl, C_periode_count, 12).tointeger();
 			local periode_type = getStatisticsPeriodeType(qs_tbl.periode_type);
-			local sab = qs_tbl.rawget("sab", "S");
+			local sab = table_rawget(qs_tbl, "sab", "S");
 			return product_bar_chart_statistics_sql(product_id.tointeger(), sab, periode_count, periode_type);
 		}
-		else if (qs_tbl.rawget("print_list", false)){
+		else if (table_rawget(qs_tbl, "print_list", false)){
 			return products_list_sql();
 		}
-		else if (qs_tbl.rawget("pdf", false)){
+		else if (table_rawget(qs_tbl, "pdf", false)){
 			return getPdfList(qs_tbl);
 		}
 	}
@@ -2774,25 +2775,25 @@ where p.id=?]==];
 	}
 
 	function sql_get_one(tbl_qs) {
-		local id = tbl_qs.get(table_name, 0).tointeger();
+		local id = table_get(tbl_qs, table_name, 0).tointeger();
 		
-		if(tbl_qs.rawget("discount_by_quantity", false))
+		if(table_rawget(tbl_qs, "discount_by_quantity", false))
 		{
-			local quantity = tbl_qs.get(C_quantity, 0);
+			local quantity = table_get(tbl_qs, C_quantity, 0);
 			if(!quantity) return;
 			return discount_by_quantity_get_one(id, quantity);
 		}
-		else if(tbl_qs.rawget("price_for_calc", false))
+		else if(table_rawget(tbl_qs, "price_for_calc", false))
 		{
 			return get_one_record_sql( _table_name, id,
                                 "buy_price, buy_discount, buy_other_costs, sell_price, price_decimals");
 		}
-		else if (tbl_qs.rawget("product_for_edit", false)){
+		else if (table_rawget(tbl_qs, "product_for_edit", false)){
 			local db = getOurbizDB();
 			local buf = blob(0, 8192);
 			local sql;
 			
-			if(tbl_qs.rawget("with_thumbnail", false)){
+			if(table_rawget(tbl_qs, "with_thumbnail", false)){
 				sql = getOneSqlWithThumbnail();
 			} else {
 				sql = base.sql_get_one(tbl_qs)
@@ -2817,7 +2818,7 @@ where p.id=?]==];
 
 			return buf;
 		}
-		else if(tbl_qs.rawget("product_aux_data", false))
+		else if(table_rawget(tbl_qs, "product_aux_data", false))
 		{
 			local db = getOurbizDB();
 			local buf = blob(0, 8192);
@@ -2833,11 +2834,11 @@ where p.id=?]==];
 
 			return buf;
 		}
-		else if(tbl_qs.rawget("pdf", false))
+		else if(table_rawget(tbl_qs, "pdf", false))
 		{
 			return getPdfList(qs_tbl);
 		}
-		else if(tbl_qs.rawget("with_thumbnail", false)){
+		else if(table_rawget(tbl_qs, "with_thumbnail", false)){
 			return getOneSqlWithThumbnail().replace("?", id.tostring());
 		}
 		else return base.sql_get_one(tbl_qs);
@@ -2845,10 +2846,10 @@ where p.id=?]==];
 
 	function db_action(db, data)
 	{
-		local str = data.rawget("__action__", false);
+		local str = table_rawget(data, "__action__", false);
 		if(str == "calc_price_by_quantity")
 		{
-			local id = data.rawget("__id__", 0);
+			local id = table_rawget(data, "__id__", 0);
 			if(!id) return;
 
 			_calc_prices.clear();
@@ -2933,7 +2934,7 @@ local DB_Images = class extends DB_Manager {
 	function sql_get_one(req){
 		local mf = blob();
 		mf.write("select id, _version_, mime_type, name, description, group_set, cdate, mdate, length(image) as img_size ");
-		if (req.rawget("image_and_size", false)) mf.write(", image ");
+		if (table_rawget(req, "image_and_size", false)) mf.write(", image ");
 		mf.write("from ", table_name ," where id=?");
 		return  mf.tostring();
 	}
@@ -2951,9 +2952,9 @@ local DB_order_types = class extends DB_Manager {
 	}
 
 	function sql_list(qs_tbl, post_tbl){
-		if (qs_tbl.rawget("short_list", false)){
+		if (table_rawget(qs_tbl, "short_list", false)){
 			local sql = "select id, description from order_types where is_active = 1 ";
-			local group_order = qs_tbl.rawget("group_order", false);
+			local group_order = table_rawget(qs_tbl, "group_order", false);
 			if (group_order && (group_order == "S" || group_order == "B")){
 				sql = format("%s and group_order='%s' ", sql, group_order);
 			}
@@ -3130,22 +3131,22 @@ local function ourbizDbGetList(request){
 	if (query_string){
 		local db = getOurbizDB();
 		local qs_tbl = parse_qs_to_table(query_string);
-		local list = qs_tbl.rawget("list", null);
-		local sab = qs_tbl.rawget("sab", "S");
+		local list = table_rawget(qs_tbl, "list", null);
+		local sab = table_rawget(qs_tbl, "sab", "S");
 		local isPost = request.info.request_method == "POST";
 		local sql, post_tbl, data;
 		if (isPost) post_tbl = get_post_fields(request, 1024);
 		else post_tbl = {};
 
-		if (!post_tbl.rawget("query_limit", false)) post_tbl.query_limit <- 50;
+		if (!table_rawget(post_tbl, "query_limit", false)) post_tbl.query_limit <- 50;
 		gmFile.clear();
 
 		if (list == "entity_groups") db_ourbiz_tables.entity_groups.get_list(db, gmFile);
 		else if (list == "product_groups") db_ourbiz_tables.product_groups.get_list(db, gmFile);
 		else if (list == "config") sql = "select id, key,value from config";
-		else if (db_ourbiz_tables.get(list, false)){
+		else if (table_get(db_ourbiz_tables, list, false)){
 			sql = db_ourbiz_tables[list].sql_list(qs_tbl, post_tbl);
-			local doc_pdf = qs_tbl.rawget("_doc_pdf_", false);
+			local doc_pdf = table_rawget(qs_tbl, "_doc_pdf_", false);
 			if (doc_pdf){
 				request.print(format([==[
 HTTP/1.1 200 OK
@@ -3198,9 +3199,9 @@ local function ourbizDbGetOne(request){
 		gmFile.clear()
 
 		if (tbl == "config") sql = "select * from config where id=?";
-		else if (db_ourbiz_tables.get(tbl, false)) sql = db_ourbiz_tables[tbl].sql_get_one(tbl_qs);
+		else if (table_get(db_ourbiz_tables, tbl, false)) sql = db_ourbiz_tables[tbl].sql_get_one(tbl_qs);
 		
-		local doc_pdf = tbl_qs.rawget("_doc_pdf_", false);
+		local doc_pdf = table_rawget(tbl_qs, "_doc_pdf_", false);
 		if (doc_pdf){
 			request.print(format([==[
 HTTP/1.1 200 OK
@@ -3246,7 +3247,7 @@ local function ourbizDbGetBin(request){
 		local db = getOurbizDB();
 		local image = request.get_var(query_string, "image");
 		local thumbnail = request.get_var(query_string, "thumbnail");
-		local etag = request.info.http_headers.rawget("If-None-Match", false);
+		local etag = table_rawget(request.info.http_headers, "If-None-Match", false);
 		local stmt;
 		gmFile.clear();
 		
@@ -3303,12 +3304,12 @@ local function ourbizDbAction(request) {
 	if (isPost){
 		local data = get_post_fields(request, 10*1024);
 		local db = getOurbizDB();
-		local tbl = data.rawget("__table__", null);
-		local action = data.rawget("__action__", null);
+		local tbl = table_rawget(data, "__table__", null);
+		local action = table_rawget(data, "__action__", null);
 		local result;
 		//debug_print(tbl, "\n", action, "\n", data.__id__, "\n")
 		gmFile.clear();
-		local db_manager = db_ourbiz_tables.get(tbl, null);
+		local db_manager = table_get(db_ourbiz_tables, tbl, null);
 		if (db_manager){
 			result = db_manager.db_action(db, data);
 		}

+ 1 - 1
SquiLu-ourbiz/pedidos2.nut

@@ -286,7 +286,7 @@ class Base_Window extends Fl_Window {
 		_db_map = {};
 	}
 	function add_input_field_to_map(tbl, fldname, fld){
-		local tbl_map = _db_map.get(tbl, false);
+		local tbl_map = table_get(_db_map, tbl, false);
 		if(!tbl_map){
 			tbl_map = {};
 			_db_map[tbl] <- tbl_map;

+ 5 - 5
SquiLu-ourbiz/search-options.nut

@@ -92,18 +92,18 @@ class OurBizSearchOptions
 
     function getOptionsFromMap(map)
     {
-        search_str = map.rawget("search_str", "");
-        select_fields = map.rawget("select_fields", "");
-        search_on = map.rawget("search_on", "1");
+        search_str = table_rawget(map, "search_str", "");
+        select_fields = table_rawget(map, "select_fields", "");
+        search_on = table_rawget(map, "search_on", "1");
 
 	foreach(k,v in this){
 		local ktype = type(v);
 		if(ktype == "integer") {
-			local value = map.get(k, null);
+			local value = table_get(map, k, null);
 			if(value != null) this[k] = value.tointeger();
 		}
 		else if(ktype == "bool"){
-			this[k] = map.get(k, null) != null;
+			this[k] = table_get(map, k, null) != null;
 		}
 	}
     }

+ 280 - 46
SquiLu-ourbiz/sq-server-plugin.nut

@@ -5,9 +5,18 @@
  */
  
 local globals = getroottable();
-if(!globals.rawget("APP_CODE_FOLDER", false)) ::APP_CODE_FOLDER <- ".";
+if(!table_rawget(globals, "APP_CODE_FOLDER", false)) ::APP_CODE_FOLDER <- ".";
 WIN32 <- os.getenv("WINDIR") != null;
 
+function debugLog(msg)
+{
+	local fd = file(APP_CODE_FOLDER + "/debug.log", "a");
+	fd.write(msg);
+	fd.write("\n");
+	fd.close();
+}
+//debugLog("starting sq-server_plugin.nut");
+
 math.srand(os.time());
 
 //local AT_DEV_DBG=true;
@@ -16,6 +25,111 @@ math.srand(os.time());
 //local EDIT_MD5_PASSWORD = md5("edit_user:r.dadbiz.es:okdoedit");
 //local VIEW_MD5_PASSWORD = md5("view_user:r.dadbiz.es:okdoview");
 
+local _sq_profile_calls_at, _sq_profile_calls, _sq_profile_total, _sq_profile_this,
+	_sq_profile_start_time, _sq_profile_end_time;
+
+local function profileReset()
+{
+	_sq_profile_calls_at = {};
+	_sq_profile_calls = {};
+	_sq_profile_total = {};
+	_sq_profile_this = {};
+	_sq_profile_start_time = 0;
+	_sq_profile_end_time = 0;
+}
+
+profileReset();
+
+local function profileDebughook(event_type,sourcefile,line,funcname)
+{
+	//local fname = format("%s:%d", funcname ? funcname : "unknown", line);
+	local fname = funcname ? funcname : "unknown";
+	local srcfile=sourcefile ? sourcefile : "unknown";
+	local fname_at = format("%s:%d:%s", fname, line, srcfile);
+	//local fname_at = fname + ":" + line + ":" + srcfile;
+	switch (event_type) {
+		//case 'l': //called every line(that contains some code)
+			//::print("LINE line [" + line + "] func [" + fname + "]");
+			//::print("file [" + srcfile + "]\n");
+		//break;
+		case 'c': //called when a function has been called
+			//::print("LINE line [" + line + "] func [" + fname + "]");
+			//::print("file [" + srcfile + "]\n");
+			table_rawset(_sq_profile_calls_at, fname_at, table_rawget(_sq_profile_calls_at, fname_at, 0) + 1);
+			table_rawset(_sq_profile_thisfname, os.clock());
+		break;
+		case 'r': //called when a function returns
+			//::print("LINE line [" + line + "] func [" + fname + "]");
+			//::print("file [" + srcfile + "]\n");
+			local time = os.clock() - table_rawget(_sq_profile_this, fname, 0);
+			table_rawset(_sq_profile_total, fname, table_rawget(_sq_profile_total, fname, 0) + time);
+			table_rawset(_sq_profile_calls, fname, table_rawget(_sq_profile_calls, fname, 0) + 1);
+		break;
+	}
+}
+
+local function profileStart()
+{
+	profileReset();
+	_sq_profile_start_time = os.clock();
+	setdebughook(profileDebughook);
+}
+
+local function profileEnd()
+{
+	setdebughook(null);
+	_sq_profile_end_time = os.clock();
+}
+
+local function profileDump()
+{
+	// print the results
+	local total_time = _sq_profile_end_time - _sq_profile_start_time;
+	print(format("Profile info: %.3f seconds", total_time));
+	local info_ary = [];
+	foreach( fname, time in _sq_profile_total )
+	{
+		if(fname == "profileStart" || fname == "profileEnd") continue;
+		local relative_time = time / (total_time / 100.0);
+		local rt_int = relative_time.tointeger();
+		local rt_frac = ((relative_time - rt_int) * 100).tointeger();
+		info_ary.append(format("%02d.%02d %% in %.3f seconds after %d calls to %s", rt_int, rt_frac, time, table_rawget(_sq_profile_calls, fname, 0), fname));
+	}
+	info_ary.sort(@(a,b) a<b ? 1 : (a>b ? -1 : 0));
+	foreach(line in info_ary)
+	{
+		print(line);
+	}
+	info_ary.clear();
+	foreach( fname, count in _sq_profile_calls_at )
+	{
+		if(fname.startswith("profileStart") || fname.startswith("profileEnd")) continue;
+		info_ary.append(format("%6d\tcalls to %s", count, fname));
+	}
+	info_ary.sort(@(a,b) a<b ? 1 : (a>b ? -1 : 0));
+	foreach(line in info_ary)
+	{
+		print(line);
+	}	
+}
+
+local _sq_time_start = 0;
+
+local function sqStartTimer()
+{
+	_sq_time_start = os.clock();
+}
+
+local function sqGetElapsedTimer()
+{
+	return os.clock() - _sq_time_start;
+}
+
+local function sqPrintElapsedTimer()
+{
+	print(format("Elapsed time %.3f seconds", sqGetElapsedTimer()));
+}
+
 class MySMTP  {
 	boundary = null;
 	smtp_server = null;
@@ -188,11 +302,11 @@ function IntToDottedIP( intip )
 	return octet.concat(".");
 }
 
-if(!globals.rawget("gmFile", false)) ::gmFile <- blob();
-if(!globals.rawget("__tplCache", false)) ::__tplCache <- {};
+if(!table_rawget(globals, "gmFile", false)) ::gmFile <- blob();
+if(!table_rawget(globals, "__tplCache", false)) ::__tplCache <- {};
 
 function getTemplate(fname, nocache){
-	local mixBase = ::__tplCache.rawget(fname, false);
+	local mixBase = ::table_rawget(__tplCache, fname, false);
 	if (!mixBase || nocache){
 		local rfn = format("%s/%s", APP_CODE_FOLDER, fname);
 		//debug_print("\n", rfn);
@@ -207,9 +321,9 @@ function getTemplate(fname, nocache){
 }
 
 /*
-if(!globals.rawget("__stmtCache", false)) ::__stmtCache <- {};
+if(!table_rawget(globals, "__stmtCache", false)) ::__stmtCache <- {};
 function getCachedStmt(db, stmt_key, sql_or_func){
-	local stmt = ::__stmtCache.rawget(stmt_key, false);
+	local stmt = ::table_rawget(__stmtCache, stmt_key, false);
 	if (!stmt){
 		//local db =checkCompaniesUkDB()
 		local sql;
@@ -237,24 +351,34 @@ function unescapeHtml ( str ){
 				if (m == "&lt;") return "<";
 				else if (m == "&gt;") return ">";
 				else if (m == "&amp;") return "&";
-				return m;
+				else if (m == "&quot;") return "\"";
+				else if (m == "&#x27;") return "'";
+				else if (m == "&#x2F;") return "/";
+				return "??";
 			});
 	}
 }
 
 function escapeHtml ( str ){
 	if (str){
-		return str.gsub("([<>&])", function(m){
+		return str.gsub("([<>&'\"/])", function(m){
 				if (m == "<") return "&lt;";
 				else if (m == ">") return "&gt;";
 				else if (m == "&") return "&amp;";
-				return m;
+				else if (m == "\"") return "&quot;";
+				else if (m == "'") return "&#x27;";
+				else if (m ==  "/") return "&#x2F;";
+				return "??";
 			});
 	}
 }
 
+
 function var2json(v){
 	switch(type(v)){
+		case "string":
+			return format("%q", v);
+			break;
 		case "table":
 			local result = [];
 			foreach(k2, v2 in v) {
@@ -285,7 +409,9 @@ function var2json(v){
 			break;
 
 		default:
-			return "\"" + v.tostring().replace("\"", "\\\"").replace("\n", "\\n") + "\"";
+			//return "\"" + v.tostring().replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t").replace("\b", "\\b").replace("\f", "\\f") + "\"";
+			//debug_print("\n", __LINE__, ":", type(v));
+			return format("%q", try_tostring(v));
 	}
 	return "";
 }
@@ -301,6 +427,60 @@ function json2var(json) {
 	return tbl;
 }
 
+function time_stamp(){
+	return os.date("!%Y-%m-%d %H:%M:%S");
+}
+
+class ObjecIdMaker
+{
+	_machine_id = null;
+	_pid = null;
+	_index = null;
+
+	constructor()
+	{
+		_machine_id =  (math.random() * 0xFFFFFF).tointeger();
+		_index = (math.random() * 0xFFFFFF).tointeger();
+		_pid = (math.random() * 100000).tointeger() % 0xFFFF;
+	}
+
+	function next(tm=null) {
+		_index = (_index+1) % 0xFFFFFF;
+		return format("%.8x%.6x%.4x%.6x",
+			(tm == null) ? os.time() : tm, _machine_id, _pid, _index);
+	}
+
+	function createFromTime(tm=null) {	
+		return format("%.8x0000000000000000", (tm == null) ? os.time() : tm);
+	}
+
+	function getTime(objectId) {
+		local p1 = objectId.slice(0, 8);
+		return p1.tointeger(16);
+	}
+
+	function getDate(objectId) {
+		local p1 = objectId.slice(0, 8);
+		return os.date("!%Y-%m-%dT%H:%M:%S.000Z", p1.tointeger(16));
+	}
+
+	
+	function getParts(oid) {
+		local tm = oid.slice(0, 8).tointeger(16);
+		local m_id = oid.slice(8, 14).tointeger(16);
+		local p_id = oid.slice(14, 18).tointeger(16);
+		local idx_id = oid.slice(18, 24).tointeger(16);
+		return [tm, m_id, p_id, idx_id];
+	}
+
+	function createFromParts(oid) {
+		return format("%.8x%.6x%.4x%.6x",
+			oid[0], oid[1], oid[2], oid[3]);
+	}
+}
+
+ObjectId <- ObjecIdMaker();
+
 function doSaveTableArrayToFD(ta, fd){
 	local function dumpValue(val){
 		local vtype = type(val);
@@ -363,6 +543,10 @@ function fillTemplate(template, data, nocache){
 	mixFunc.call(data);
 }
 
+function getFfileName(full_path) {
+	return full_path.match("([^/]+)$");
+}
+
 //
 // Post
 //
@@ -375,8 +559,8 @@ function split_filename(path){
   return result;
 }
 
-function insert_field (dest, key, value){
-  local fld = dest.rawget(key, null);
+function form_url_insert_field (dest, key, value){
+  local fld = table_rawget(dest, key, null);
   if (!fld) dest[key] <- value;
   else
   {
@@ -395,7 +579,7 @@ function multipart_data_get_field_names(headers, name_value){
 	return true;
   });
   name_value.push(attrs.name);
-  name_value.push(attrs.rawget("filename", false) ? split_filename(attrs.filename) : null);
+  name_value.push(table_rawget(attrs, "filename", false) ? split_filename(attrs.filename) : null);
 }
 
 function multipart_data_break_headers(header_data){
@@ -474,8 +658,9 @@ function parse_multipart_data(input, input_type, tab=null){
 	input.find_lua(state.boundary, function(start, end){state.pos <- end+1;return false;}, 0, true);
 	while(true){
 		local name_value = multipart_data_parse_field(input, state);
+		//debug_print("\nparse_multipart_data: ", name_value);
 		if(!name_value) break;
-		insert_field(tab, name_value[0], name_value[1]);
+		form_url_insert_field(tab, name_value[0], name_value[1]);
 	}
 	return tab;
 }
@@ -486,7 +671,7 @@ function parse_qs(qs, tab=null){
 		//debug_print(qs)
 		qs.gmatch("([^&=]+)=([^&=]*)&?", function(key,val){
 			//debug_print(key, "->", val)
-			insert_field(tab, url_decode(key), url_decode(val));
+			form_url_insert_field(tab, url_decode(key), url_decode(val));
 			return true;
 		});
 	}
@@ -546,9 +731,10 @@ function parse_post_data(input_type, data, tab = null){
 	return tab;
 }
 
-function get_post_fields(request, max_len=1024*1000){
+function get_post_fields(request, max_len=1024*1000, post_fields=false){
 	local data_len = (request.get_header("Content-Length") || "0").tointeger();
-	local post_fields = {};
+	if(!post_fields) post_fields = {};
+	//debug_print("\nget_post_fields: ", __LINE__, ":", data_len, ":", max_len);
 	if (data_len > 0 && data_len <= max_len) {
 		local content_type = request.get_header("Content-Type") || "x-www-form-urlencoded";
 		local data = request.read(data_len);
@@ -559,7 +745,7 @@ function get_post_fields(request, max_len=1024*1000){
 		fd.close();
 		debug_print(request.get_header("Content-Type"), "\n");
 */
-		if(content_type == "application/json; charset=UTF-8"){
+		if(content_type.find("application/json") >= 0){
 			return data;
 		}
 		parse_post_data(content_type, data, post_fields);
@@ -571,12 +757,13 @@ local allowedUploadFileExtensions = {
 	[".png"] = "image/png",
 	[".jpg"] = "image/jpeg",
 	[".gif"] = "image/gif",
+	[".svg"] = "image/svg+xml",
 }
 
 function getMimeType(fname){
 	local ext;
 	fname.gmatch("(%.?[^%.\\/]*)$", @(m) ext=m);
-	if( ext ) return allowedUploadFileExtensions.rawget(ext, "unknown");
+	if( ext ) return table_rawget(allowedUploadFileExtensions, ext, "unknown");
 	return "unknown";
 }
 
@@ -616,7 +803,7 @@ local allowedEditFileExtensions = {
 function isExtensionAllowed(fname){
 	local ext;
 	fname.gmatch("(%.?[^%.\\/]*)$", @(m) ext=m);
-	if( ext ) return allowedEditFileExtensions.rawget(ext, false);
+	if( ext ) return table_rawget(allowedEditFileExtensions, ext, false);
 	return false;
 }
 
@@ -629,14 +816,16 @@ function getFilesInPath(path, files=null, prefix=""){
 			if (prefix.len() > 0) pf = prefix + "/" + file;
 			else pf = file;
 
-			local attr = sqfs.attributes (f);
+			try {
+				local attr = sqfs.attributes (f);
 
-			if(attr.mode == "directory") getFilesInPath (f, files, pf);
-			else
-			{
-				if( isExtensionAllowed(pf) ) files.push(pf);
-				//foreach(name, value in attr) print (name, value);
-			}
+				if(attr.mode == "directory") getFilesInPath (f, files, pf);
+				else
+				{
+					if( isExtensionAllowed(pf) ) files.push(pf);
+					//foreach(name, value in attr) print (name, value);
+				}
+			} catch(e) {}
 		}
 	}
 	files.sort();
@@ -690,20 +879,29 @@ function send_http_error_500(request, err_msg){
 }
 
 local uri_handlers = {
+	["/SQ/hello-world"] = function(request){
+		local hello = "Hello World !\n";
+		local resp = format("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8;\r\nContent-Length: %d\r\n\r\n%s", hello.len(), hello)
+		request.print(resp)
+		return true;
+	},
 	["/SQ/testParams"] = function(request){
-		request.print("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n")
-		request.print("<html><body><h1>Request Info</h1><ul>")
+		local mFile = gmFile;
+		mFile.clear(); //allways reset global vars
+		mFile.write("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n")
+		mFile.write("<html><body><h1>Request Info</h1><ul>")
 		foreach(k, v in request.info) {
 			if ("table" == type(v) ){
-				request.print(format("<li><b>%s</b>:</li><ul>", k));
+				mFile.write(format("<li><b>%s</b>:</li><ul>", k));
 				foreach( k2, v2 in v){
-					request.print(format("<li><b>%s</b>: %s</li>", k2, v2));
+					mFile.write(format("<li><b>%s</b>: %s</li>", k2, v2));
 				}
-				request.print("</ul>");
+				mFile.write("</ul>");
 			}
-			else request.print(format("<li><b>%s</b>: %s</li>", k, (v == NULL ? "" : v).tostring()));
+			else mFile.write(format("<li><b>%s</b>: %s</li>", k, (v == NULL ? "" : v).tostring()));
 		}
-		request.print("</ul></body></html>");
+		mFile.write("</ul></body></html>");
+		request.write_blob(mFile);
 		return true;
 	},
 	["/SQ/logout"] = function(request){
@@ -716,8 +914,8 @@ local uri_handlers = {
 		//password protected
 		bool_t canEdit = false;
 		//print("EDIT_MD5_PASSWORD=", EDIT_MD5_PASSWORD, "\n")
-		bool_t isViewOnly = globals.rawget("VIEW_MD5_PASSWORD", false) && request.check_password(VIEW_MD5_PASSWORD);
-		if (!isViewOnly) canEdit = globals.rawget("EDIT_MD5_PASSWORD", false) && request.check_password(EDIT_MD5_PASSWORD);
+		bool_t isViewOnly = table_rawget(globals, "VIEW_MD5_PASSWORD", false) && request.check_password(VIEW_MD5_PASSWORD);
+		if (!isViewOnly) canEdit = table_rawget(globals, "EDIT_MD5_PASSWORD", false) && request.check_password(EDIT_MD5_PASSWORD);
 
 		if(!(canEdit || isViewOnly) ) {
 			request.send_authorization_request("r.dadbiz.es");
@@ -733,8 +931,8 @@ local uri_handlers = {
 		bool_t isPost = request.info.request_method == "POST";
 		if (isPost && canEdit) {
 			local post_fields = get_post_fields(request);
-			if (post_fields.rawget("save", false)) {
-				local content = post_fields.rawget("content", null);
+			if (table_rawget(post_fields, "save", false)) {
+				local content = table_rawget(post_fields, "content", null);
 				if (content){
 					data.file_name <- sanitizePath(post_fields.file_name);
 					if (!isExtensionAllowed(data.file_name)) data.file_name = NULL;
@@ -804,16 +1002,27 @@ function apply_uri_filters(request){
 	return false;
 }
 
-if(AT_DEV_DBG || !globals.rawget("MyCompaniesUkLoaded", false)) {
+function sendJson(request, response, extra_headers=""){
+	request.print(format("HTTP/1.1 200 OK\r\nServer: SquiluAppServer\r\nContent-Type: text/json; charset=utf-8\r\nCache-Control: no-cache\r\nContent-Length: %d\r\n%s\r\n%s", response.len(), extra_headers, response));
+	return true;
+}
+
+function sendJs(request, response, extra_headers=""){
+	request.print(format("HTTP/1.1 200 OK\r\nServer: SquiluAppServer\r\nContent-Type: application/x-javascript; charset=utf-8\r\nCache-Control: no-cache\r\nContent-Length: %d\r\n%s\r\n%s", response.len(), extra_headers, response));
+	return true;
+}
+
+
+if(AT_DEV_DBG || !table_rawget(globals, "MyCompaniesUkLoaded", false)) {
 	dofile(APP_CODE_FOLDER + "/companies-uk.nut");
 }
 
-if(AT_DEV_DBG || !globals.rawget("MyOurBizLoaded", false)) {
+if(AT_DEV_DBG || !table_rawget(globals, "MyOurBizLoaded", false)) {
 	dofile(APP_CODE_FOLDER + "/ourbiz.nut");
 }
 
-if(AT_DEV_DBG || !globals.rawget("MyOurShoppingCartLoaded", false)) {
-	dofile(APP_CODE_FOLDER + "/ourbiz-shopping-cart.nut");
+if(AT_DEV_DBG || !table_rawget(globals, "MyOurShoppingCartLoaded", false)) {
+	//dofile(APP_CODE_FOLDER + "/ourbiz-shopping-cart.nut");
 }
 
 local ourbiz_password = md5("mingote:ourbiz.dadbiz.es:tr14pink");
@@ -821,19 +1030,44 @@ function handle_request(request){
 	//static content served by mongoose directly
 	local request_uri = request.info.uri;
 	//debug_print(request.get_option("document_root"), "::", request_uri, "\n")
-	
+	/*
+	local ext = request_uri.match("%.%w+$");
+	switch(ext)
+	{
+		case ".jpg":
+		case ".png":
+		case ".svg":
+		case ".js":
+		case ".css":
+		case ".mp4":
+		case ".webm":
+		case ".ico":
+		case ".php":
+		case ".html":
+			return false;
+
+	}
+	*/
+
 	if(apply_uri_filters(request)) {
 		return true;
 	}
 	
 	if(request_uri.startswith("/DB/")){
 		if(!request.check_password(ourbiz_password)) {
-			request.send_authorization_request("ourbiz.dadbiz.es");
-			return true;
+			//request.send_authorization_request("ourbiz.dadbiz.es");
+			//return true;
 		}
 	}
 	if (request_uri.endswith(".js") || request_uri.endswith(".css") ) return false;
 	if (request_uri == "/index.html" || request_uri == "/" ) return uri_handlers["/search"](request);
-	if( uri_handlers.rawget(request_uri, false) ) return uri_handlers[request_uri](request);
+	if( table_rawget(uri_handlers, request_uri, false) ) return uri_handlers[request_uri](request);
 	return false;
 }
+
+if(table_rawin(globals, "addExtraWebAppCode"))
+{
+	//debugLog("call  addExtraWebAppCode");
+	//addExtraWebAppCode(this);
+}
+//debugLog("end of sq-server_plugin.nut");

+ 52 - 19
SquiLu-ourbiz/sq-server.nut

@@ -5,7 +5,7 @@
  */
  
 local globals = getroottable();
-if(!globals.rawget("APP_CODE_FOLDER", false)) ::APP_CODE_FOLDER <- ".";
+if(!table_rawget(globals, "APP_CODE_FOLDER", false)) ::APP_CODE_FOLDER <- ".";
 
 WIN32 <- os.getenv("WINDIR") != null;
 
@@ -23,20 +23,31 @@ function getUserCallbackSetup(fn){
 	local code = fd.read(fd.len());
 	fd.close();
 	local extra_code = format("APP_CODE_FOLDER <- \"%s\";\n", APP_CODE_FOLDER);
+	
+	local checkGlobal = function(gv)
+	{
+		if (table_rawin(globals, gv)){
+			extra_code += format(gv + " <- \"%s\";\n", table_rawget(globals, gv));
+		} else extra_code += gv + " <- false;\n";
+	}
 
-	if (globals.rawget("VIEW_MD5_PASSWORD", false)){
-		extra_code += format("VIEW_MD5_PASSWORD <- \"%s\";\n", VIEW_MD5_PASSWORD);
-	} else extra_code += "VIEW_MD5_PASSWORD <- false;\n";
-
-	if (globals.rawget("EDIT_MD5_PASSWORD", false)){
-		extra_code += format("EDIT_MD5_PASSWORD <- \"%s\";\n", EDIT_MD5_PASSWORD);
-	} else extra_code += "EDIT_MD5_PASSWORD <- false;\n";
-
-	if (globals.rawget("AT_DEV_DBG", false)){
-		extra_code += "AT_DEV_DBG <- true;\n"
-	} else extra_code += "AT_DEV_DBG <- false;\n"
+	checkGlobal("GLOBAL_PASSWORD_DOMAIN");
+	checkGlobal("GLOBAL_MD5_PASSWORD");
+	checkGlobal("VIEW_MD5_PASSWORD");
+	checkGlobal("EDIT_MD5_PASSWORD");
+	checkGlobal("EDIT_MD5_PASSWORD");
+
+	if (table_rawget(globals, "AT_DEV_DBG", false)){
+		extra_code += "AT_DEV_DBG <- true;\n";
+	} else extra_code += "AT_DEV_DBG <- false;\n";
+	
+	code = extra_code + "\n" + code;
+	if(table_rawin(globals, "addExtraCodeToUserCallbackSetup"))
+	{
+		code = addExtraCodeToUserCallbackSetup() + code;
+	}
 
-	return compilestring(format("%s\n%s", extra_code, code));
+	return compilestring( code );
 }
 
 local mongoose_start_params = {
@@ -44,9 +55,9 @@ local mongoose_start_params = {
 	listening_ports = "8080",
 	document_root = "./s",
 	//num_threads = 50,
-	enable_keep_alive = "yes",
-	enable_tcp_nodelay = "yes",
-	request_timeout_ms = "30000",
+	//enable_keep_alive = "yes",
+	//enable_tcp_nodelay = "yes",
+	//request_timeout_ms = "30000",
 
 	//cgi_extensions = "lua",
 	//cgi_interpreter = "/usr/bin/lua",
@@ -69,17 +80,39 @@ local mongoose_start_params = {
 	user_callback = function(event, request){
 		//debug_print("\nevent :\n", event);
 		if(event == "MG_NEW_REQUEST"){
+			if(GLOBAL_MD5_PASSWORD)
+			{
+				if(request.info.uri == "/SQ/logout")
+				{
+					request.close_session();
+					request.print(format("HTTP/1.1 302 Found\r\nLocation: http%s://%s\r\n\r\n", 
+						request.info.is_ssl ? "s" : "", request.info.http_headers.Host));
+					return true;
+				}
+/*
+				if(!request.check_password(GLOBAL_MD5_PASSWORD)) {
+					request.send_authorization_request(GLOBAL_PASSWORD_DOMAIN);
+					return true;
+				}*/
+			}
 			//debug_print("\n", request.get_option("num_threads"), request.get_conn_buf());
-			if(AT_DEV_DBG || !this.get("handle_request", false)) {
+			if(AT_DEV_DBG || !table_get(this, "handle_request", false)) {
+				 //when developing we reload everything on each request
 				dofile(APP_CODE_FOLDER + "/sq-server-plugin.nut");
 			}
+			local result;
 			try {
 				//debug_print("\nHttp :\n", request.info.uri);
-				return handle_request(request);
+				result = handle_request(request);
 			}
 			catch(exep){
-				return send_http_error_500(request, exep);
+				result = send_http_error_500(request, exep);
+			}
+			if(AT_DEV_DBG && table_get(this, "onDevCleanup", false)) {
+				 //when developing if we need to cleanup something here is the place
+				onDevCleanup();
 			}
+			return result;
 		}
 		else if(event == "MG_EVENT_LOG"){
 			//debug_print("\n", request.info.log_message);

+ 999 - 0
SquiLu-ourbiz/sqlite-utils.nut

@@ -0,0 +1,999 @@
+class SQLiteUtils {
+	
+	static __db_cache = {};
+
+	static function getDbFor(dbname)
+	{
+		local db = table_rawget(__db_cache, dbname, false);
+		if(!db)
+		{
+			db =  SQLite3(dbname);
+			//db.exec_dml("PRAGMA synchronous = 0;");
+			//db.exec_dml("PRAGMA journal_mode = WAL");
+			table_rawset(__db_cache, dbname, db);
+		}
+		return db;
+	}
+
+	static __query_cache = {};
+
+	static function getCachedQuery(qfn, db, sql)
+	{
+		return qfn(db, sql);
+		/*
+		There is a problem here we need to cache by db also
+		local result = table_rawget(__query_cache, sql, false);
+		if(!result)
+		{
+			result = qfn(db, sql);
+			__query_cache[sql] <- result;
+		}
+		return result;
+		*/
+	}
+
+	static function escapeRE(str)
+	{
+		return str.gsub("(%-)", "%%%1");
+	}
+
+	static function escape_sql_like_search_str(str){
+		if (str && (str.len() > 0)){
+			str = str.gsub("%%", "%%%%");
+			if (str.find(" ") == 0)
+				str = str.gsub("^%s*(.+)%s*$","%1%%");
+			else
+				str = str.gsub("^%s*(.+)%s*$","%%%1%%");
+		}
+		//print("escape_sql_like_search_str +" + str + "+")
+		return str;
+	}
+	
+	static function sanitizeDBName(dbname)
+	{
+		return dbname.gsub("([^_%-a-zA-Z0-9])","");
+	}
+	
+	static function create_stmt_bind(db, sql, bind_values=null)
+	{
+		local result = false;
+		local stmt = db.prepare(sql);
+		if(bind_values)
+		{
+			foreach(k,v in bind_values)
+			{
+				stmt.bind(k+1, v);
+			}
+		}
+		return stmt;
+	}
+
+	static function exec_get_all(db, sql, bind_values=null)
+	{
+		local stmt = create_stmt_bind(db, sql, bind_values);
+		local result = stmt.asArrayOfTables();
+		stmt.finalize();
+		return result;
+	}
+
+	static function exec_get_one(db, sql, bind_values=null)
+	{
+		local result = null;
+		local stmt = create_stmt_bind(db, sql, bind_values);
+		if(stmt.next_row())
+		{
+			result = stmt.col(0);
+		}
+		stmt.finalize();
+		return result;
+	}
+
+	static function exec_dml(db, sql, bind_values=null)
+	{
+		local stmt = create_stmt_bind(db, sql, bind_values);
+		//debug_print("exec_dml: ", sql, "\n");
+		local result = stmt.step() == stmt.SQLITE_DONE;
+		stmt.finalize();
+		return result;
+	}
+	
+	static function getReadOnlyFields(db, attached_db="")
+	{
+		local read_only_fields = {};
+		local ro = exec_get_all(db, format("SELECT * FROM %s__fields_metadata_sys_names_view", attached_db));
+		if(ro && ro.len())
+		{
+			foreach(k,v in ro[0])
+			{
+				table_rawset(read_only_fields, v, true);
+			}
+		}
+		return read_only_fields;
+	}
+
+	static function getAcceptFieldsFields(db, tbl_name, attached_db="")
+	{
+		local af = {};
+		local fld_name_key = "field_id_name";
+		local all_af = exec_get_all(db, format("SELECT \"%s\" FROM %s__table_metadata_accept_fields_view WHERE table_id_name=?", fld_name_key, attached_db), [tbl_name]);
+		if(all_af && all_af.len())
+		{
+			foreach(k,v in all_af)
+			{
+				table_rawset(af, table_rawget(v, fld_name_key), true);
+			}
+		}
+		return af;
+	}
+	
+	static function getTableListFields(db, tbl_name, attached_db="")
+	{
+		local fld_name_key = "field_id_name";
+		local fields = exec_get_one(db, format("SELECT group_concat(\"%s\", ',') AS fields FROM %s__table_metadata_fields_view0 WHERE table_id_name=? AND hide_on_list <> 1", fld_name_key, attached_db), [tbl_name]);
+		return fields;
+	}
+	
+	static function getEditLinkFields(db, table_name, attached_db="")
+	{
+		local rows = exec_get_all(db, format("SELECT * FROM %s__table_metadata_edit_links_view WHERE table_id_name=?", attached_db), [table_name]);
+		local result = {};
+		foreach(k,row in rows)
+		{
+			result[row.field_id_name] <- row;
+		}
+		return result;
+	}
+
+	static function getTableListInstead(db, the_table_name, attached_db="")
+	{
+		local list_instead = exec_get_one(db, 
+			format("SELECT list_table_id_name FROM %s__tables_metadata_view WHERE name=?", attached_db), 
+			[the_table_name]);
+		if(::type(list_instead) == "string")
+		{
+			return list_instead;
+		}
+		return the_table_name;
+	}
+	
+	static function getTableEditInstead(db, the_table_name, attached_db="")
+	{
+		local edit_instead = exec_get_one(db, 
+			format("SELECT edit_table_id_name FROM %s__tables_metadata_view WHERE name=?", attached_db), 
+			[the_table_name]);
+		if(::type(edit_instead) == "string")
+		{
+			return edit_instead;
+		}
+		return the_table_name;
+	}
+	
+	static function getListLimitForTableName(db, the_table_name, attached_db="")
+	{
+		the_table_name = getTableEditInstead(db, the_table_name, attached_db);
+		local limit = exec_get_one(db, format("SELECT default_list_limit FROM %s__tables_metadata WHERE name =?", attached_db), [the_table_name]);
+		return limit ? limit : 0;
+	}
+	
+	static function getIsReadOnlyForTableName(db, the_table_name, attached_db="")
+	{
+		local is_read_only = exec_get_one(db, format("SELECT is_read_only FROM %s__tables_metadata WHERE name=?", attached_db), [the_table_name]);
+		return is_read_only != null ? is_read_only : 1; //if not found it's read only
+	}
+
+	static function getFilterTables(db, table_name, attached_db="")
+	{
+		local rows = exec_get_all(db, format("SELECT * FROM %s__table_metadata_filter_tables_list_view WHERE table_id_name=?", attached_db), [table_name]);
+		return rows;
+	}
+
+	static function getByIdFieldAsBlob(db, table_name, id, field_name, attached_db="")
+	{
+		local result = exec_get_one(db, format("SELECT \"%s\" FROM %s\"%s\" WHERE \"id\"=?", 
+					sanitizeDBName(field_name), attached_db, table_name), [id]);
+		return result;
+	}
+
+	static function getSchemaSqlFor(tbl, attached_db="")
+	{
+		local str = format("SELECT sql FROM %ssqlite_master WHERE (type='table' OR type='view') AND tbl_name='%s'", attached_db, tbl);
+		return str;
+	}
+	
+	static function getSchemaFor(db, tbl, attached_db="")
+	{
+		local str = db.exec_get_one(getSchemaSqlFor(tbl, attached_db));
+		return str;
+	}
+	
+	static function getIndexesSqlFor(tbl, attached_db="")
+	{
+		local str = format("SELECT name, sql FROM %ssqlite_master WHERE type='index' AND tbl_name='%s' ORDER BY name;", attached_db, tbl);
+		return str;
+	}
+	
+	static function getTriggersSqlFor(tbl, attached_db="")
+	{
+		local str = format("SELECT name, sql FROM %ssqlite_master WHERE type='trigger' AND tbl_name='%s' ORDER BY name;", attached_db, tbl);
+		return str;
+	}
+	
+	static function getTableInfoSqlFor(tbl, attached_db="")
+	{
+		local str = format("PRAGMA %stable_info(\"%s\");", attached_db, tbl);
+		return str;
+	}
+	
+	static function getTableFieldsInfoFor(db, tbl, attached_db="")
+	{
+		local stmt = db.prepare(getTableInfoSqlFor(tbl, attached_db));
+		local fields = stmt.colsAsArray();
+		stmt.finalize();
+		return fields;
+	}
+
+	static function getFieldsFor(db, tbl, attached_db="")
+	{
+		local stmt = db.prepare(format("select * from %s\"%s\"", attached_db, tbl));
+		local fields = stmt.colsAsArray();
+		stmt.finalize();
+		return fields;
+	}
+	
+	static function getFieldsAsCSVFor(db, tbl, attached_db="")
+	{
+		local fields = getFieldsFor(db, tbl, attached_db);
+		local str_fields =  "\"" + fields.concat("\", \"") + "\"";
+		return str_fields;
+	}
+
+	static function getIndexesAndTriggersFor(db, tbl, result, attached_db="")
+	{
+		local stmt = db.prepare(getIndexesSqlFor(tbl, attached_db));
+		while(stmt.next_row())
+		{
+			local str = stmt.col(1);
+			if(::type(str) == "string") result.write("\n\n", str, ";");
+		}
+		stmt.finalize();
+
+		stmt = db.prepare(getTriggersSqlFor(tbl, attached_db));
+		while(stmt.next_row())
+		{
+			local str = stmt.col(1);
+			if(::type(str) == "string") result.write("\n\n", str, ";");
+		}
+		stmt.finalize();
+	}
+
+	static function createQuery(db, tbl, query_type, max_rows, attached_db=""){
+		if(db){
+			local sql;
+			local fields = getFieldsFor(db, tbl, attached_db);
+			local fields_csv = "\"" + fields.concat("\", \"") + "\"";
+			
+			local genSchemaUpgrade = function(db, with_references)
+			{
+				local new_suffix = "___new";
+				local old_suffix = "";
+				local result = blob(0, 8000);
+				result.write("PRAGMA foreign_keys=OFF;\n\nBEGIN;\n\n");
+				local str_schema = getSchemaFor(db, tbl, attached_db);
+				local tbl_name_suffixed = tbl + new_suffix;
+				str_schema = str_schema.gsub("(" + escapeRE(tbl) + ")", tbl_name_suffixed, 1);
+				str_schema = str_schema.gsub("(\"?" + escapeRE(tbl_name_suffixed) + "\"?)", attached_db + "%1", 1);
+				str_schema = str_schema.gsub("\n%s+", "\n\t");
+				result.write(str_schema, ";");
+				fields_csv = fields_csv.gsub(", ", ",\n\t");
+				result.write(format("\n\nINSERT INTO %s\"%s%s\"(\n\t%s\n\t)\nSELECT\n\t%s\nFROM %s\"%s%s\";", 
+					attached_db, tbl, new_suffix, fields_csv, fields_csv, attached_db, tbl, old_suffix));
+				result.write("\n\nDROP TABLE ", attached_db,"\"", tbl, old_suffix, "\";");
+				result.write("\n\nALTER TABLE ", attached_db, "\"", tbl, new_suffix, "\" RENAME TO \"", tbl, old_suffix, "\";");
+				
+				getIndexesAndTriggersFor(db, tbl, result, attached_db);
+				
+				if(with_references)
+				{
+					result.write("\n\nDROP VIEW \"view_name\";\n\n");
+					result.write(getReferencesOnDBSchema(db, tbl, attached_db));
+					result.write("\n\nCREATE VIEW  \"view_name\"  AS \"db_table_name\";");
+				}
+				result.write("\n\nPRAGMA foreign_key_check;\n\nCOMMIT;\n\nPRAGMA foreign_keys=ON;");
+				return result.tostring();
+			};
+			
+			if(query_type == "select")
+			{
+				local alias_letter = 'a';
+				local myjoins = "";
+				local fields_last_idx = fields.len()-1;
+
+				local stmt = db.prepare(format("PRAGMA %sforeign_key_list(\"%s\")", attached_db, tbl));
+				local last_fk_id = -1;
+				while(stmt.next_row())
+				{
+					local fk_id = stmt.col(0);
+					local ftable = stmt.col(2);
+					local ffrom = stmt.col(3);
+					local fto = stmt.col(4);
+					local field_idx = fields.find(ffrom);
+					local is_new_join = last_fk_id != fk_id;
+					if(is_new_join) ++alias_letter;
+					if(field_idx)
+					{
+						fields[field_idx] += format("\"%s --%c.\"%s", (fields_last_idx == field_idx ? "" : ","), alias_letter, fto);
+					}
+					if(is_new_join)
+					{
+						myjoins += format("\n--LEFT JOIN %s\"%s\" AS %c ON a.\"%s\" = %c.\"%s\"", attached_db, ftable, alias_letter, ffrom, alias_letter, fto);
+					}
+					else
+					{
+						myjoins += format(" AND a.\"%s\" = %c.\"%s\"", ffrom, alias_letter, fto);
+					}
+					last_fk_id = fk_id;
+				}
+				stmt.finalize();
+
+				fields_csv = "a.\"" + fields.concat("\",\n\ta.\"") + "\"";
+				sql = format("--CREATE VIEW %s\"%s_list_view\" AS\nSELECT\n\t%s\nFROM %s\"%s\" AS a\nLIMIT %d", 
+					attached_db, tbl, fields_csv, attached_db, tbl, max_rows.tointeger());
+				sql += myjoins;
+			}
+			else if(query_type == "insert") sql = format("INSERT INTO %s\"%s\"(%s)\nVALUES(%s)", attached_db, tbl, fields_csv, fields_csv);
+			else if(query_type == "update") {
+				fields_csv = "\"" + fields.concat("\"=?, \"") + "\"";
+				sql = format("UPDATE %s\"%s\" SET %s=?\nWHERE \"id\"=?", attached_db, tbl, fields_csv);
+			}
+			else if(query_type == "delete") sql = format("DELETE FROM %s\"%s\" WHERE \"id\"=?", attached_db, tbl);
+			else if(query_type == "create index") sql = format("CREATE INDEX %s\"%s_idx\" ON %s\"%s\"(\"field\" COLLATE NOCASE)", attached_db, tbl, attached_db, tbl);
+			else if(query_type == "create trigger") sql = format("CREATE TRIGGER %s\"%s_trigger\"\nBEFORE/AFTER/INSTEAD OF INSERT, UPDATE, DELETE OF col_name ON %s\"%s\"\nFOR EACH ROW WHEN expr\nBEGIN\nEND;", 
+							attached_db, tbl, attached_db, tbl);
+			else if(query_type == "drop table")
+			{
+				local table_type = db.exec_get_one(format("SELECT \"type\" FROM %ssqlite_master WHERE name='%s'", attached_db, tbl));
+				sql = format("DROP %s %s\"%s\"", table_type, attached_db, tbl);
+			}
+			else if(query_type == "dump table")
+			{
+				local result = blob(0, 8000);
+				local str_schema = getSchemaFor(db, tbl, attached_db);
+				str_schema = str_schema.gsub("\n%s+", "\n\t");
+				result.write("BEGIN;\n\n", str_schema, ";");
+				getIndexesAndTriggersFor(db, tbl, result, attached_db);
+				fields_csv = fields_csv.gsub(", ", ",\n\t");
+				result.write(format("\n\nINSERT INTO %s\"%s\" (\n\t%s\n\t) VALUES", attached_db, tbl, fields_csv));
+
+				local result_size = result.len();
+				local stmt = db.prepare(format("SELECT * FROM %s\"%s\"", attached_db, tbl));
+				local col_count = stmt.col_count();
+				
+				local SQLITE_INTEGER = stmt.SQLITE_INTEGER;
+				local SQLITE_FLOAT = stmt.SQLITE_FLOAT;
+				local SQLITE_NULL = stmt.SQLITE_NULL;
+				local SQLITE_TEXT = stmt.SQLITE_TEXT;
+				local SQLITE_BLOB = stmt.SQLITE_BLOB;
+				
+				while(stmt.next_row())
+				{
+					result.write("\n(");
+					for(local i=0; i < col_count; ++i)
+					{
+						local value = stmt.col(i);
+						if(i) result.write(",");
+						
+						local ctype = stmt.col_type(i);
+
+						if(ctype == SQLITE_INTEGER  || ctype == SQLITE_FLOAT)
+							result.write(value.tostring());
+							
+						else if(ctype == SQLITE_NULL) result.write("NULL");
+						else if(ctype == SQLITE_TEXT) result.write("'", value.gsub("'", "''") ,"'");
+						else if(ctype == SQLITE_BLOB) result.write(format("%q", value));
+						else result.write("??");
+					}
+					result.write("),");
+				}
+				stmt.finalize();
+				
+				if(result_size < result.len())
+				{
+					result.resize(result.len()-1); //delete last comma
+					result.write(";");
+				}
+				
+				result.write("\n\nCOMMIT;");
+				sql = result.tostring();
+			}
+			else if(query_type == "references") sql = tbl;
+			else if(query_type == "schema update") {
+				sql = genSchemaUpgrade(db, true);
+			}
+			else if(query_type == "schema update norefs") {
+				sql = genSchemaUpgrade(db, false);
+			}
+			else if(query_type == "sqlite_master update") {
+				local result = blob(0, 8000);
+				local schema_version = getSchemaVersion(db, attached_db);
+				result.write("BEGIN;\n--PRAGMA ", attached_db, "schema_version; --> ", schema_version.tostring(), "\n\n");
+				result.write("PRAGMA ", attached_db, "writable_schema=ON;\n\n");
+				
+				local stmt = db.prepare("select rowid, sql from sqlite_master where tbl_name = '" + tbl + "';");
+				while(stmt.next_row())
+				{
+					local str_schema = stmt.col(1);
+					if(::type(str_schema) == "string")
+					{
+						str_schema = str_schema.gsub("\n%s+", "\n\t");
+						str_schema = str_schema.gsub("'", "''");
+						result.write("UPDATE ", attached_db, "sqlite_master\nSET sql='", str_schema, "'\nWHERE rowid=", stmt.col(0), ";\n\n"); 
+					}
+				}				
+				stmt.finalize();
+				
+				result.write("\n\nDROP VIEW \"view_name\";\n\n");
+				result.write(getReferencesOnDBSchema(db, tbl, attached_db));
+				result.write("\n\nCREATE VIEW  \"view_name\"  AS \"db_table_name\";");
+				result.write("\n\nPRAGMA ", attached_db, "schema_version=", (schema_version.tointeger() + 1).tostring(), 
+					";\n\nPRAGMA ", attached_db, "writable_schema=OFF;\n\nPRAGMA ", attached_db, "integrit_check;\n\nCOMMIT;");
+				sql = result.tostring();
+			}
+			return sql;
+		}
+	}
+	
+	static function executeQuery(db, query_type, sql, attached_db=""){
+		local result = {query_type=query_type, result=""};
+		if(sql && sql.len()) {
+			local start_time;
+			local foreign_keys_saved = null;
+			switch(query_type)
+			{
+				case "references":
+					local references = getReferencesOnDBSchema(db, sql, attached_db);
+					table_rawset(result, "value", references);
+					table_rawset(result, "result", "OK");
+				break;
+
+				case "search all tables":
+					local references = searchOnAllTables(db, sql, iMaxRows->value().tointeger(), attached_db);
+					table_rawset(result, "value", references);
+					table_rawset(result, "result", "OK");
+				break;
+
+				case "schema update":
+				case "schema update norefs":
+					foreign_keys_saved = db.exec_get_one(format("PRAGMA %sforeign_keys", attached_db));
+				case "insert":
+				case "update":
+				case "delete":
+				case "create index":
+				case "create trigger":
+				case "sqlite_master update":
+				case "drop table":
+					try
+					{
+						start_time = os.clock();
+
+						db.exec_dml(sql);
+
+						table_rawset(result, "time_spent", os.clock() - start_time);
+						table_rawset(result, "result", "OK");
+
+						switch(query_type)
+						{
+							case "create index":
+							case "create trigger":
+							case "schema update":
+							case "schema update norefs":
+							case "sqlite_master update":
+							case "drop table":
+								table_rawset(result, "schema_version", getSchemaVersion(db, attached_db));
+							break;
+						}						
+					}
+					catch(e)
+					{
+						if(!db.IsAutoCommitOn() || query_type.startswith("schema update")) db.exec_dml("ROLLBACK;");
+						if(foreign_keys_saved != null) db.exec_dml(format("PRAGMA %sforeign_keys=" + foreign_keys_saved, attached_db));
+						table_rawset(result, "result", e);
+					}
+				break;
+				
+				case "dump table":
+				break;
+
+				default:
+			}
+		}
+		return result;
+	}
+	
+	static function getSchemaVersion(db, attached_db="")
+	{
+		return db.exec_get_one(format("PRAGMA %sschema_version", attached_db));
+	}
+	
+	static function get_tables(db, attached_db=""){
+		local sql = format("select rowid as 'rowid|ID|0', name as 'name|Name|-1', type as 'type|Type|4' from %ssqlite_master where (type='table' OR type='view') order by name", attached_db);
+		get_records_by_sql(grid_tables, sql, true);
+		//_the_schema_version = getSchemaVersion();
+	}
+	
+	static function get_records_by_sql(db, sql , named=false){
+		local stmt = db.prepare(sql);
+		local rec_list = stmt.asArrayOfArrays(SQLite3Stmt.WITH_COL_NAMES | 
+			SQLite3Stmt.AS_STRING_ALWAYS | SQLite3Stmt.NULL_AS_EMPTY_STR);
+		stmt.finalize();
+	}
+
+	static function doUpdateTablesMetadataFor(db, all_tables, attached_db="")
+	{
+		local stmt_insert_table = db.prepare(format("INSERT INTO %s__tables_metadata(name, is_view) VALUES(?,?)", attached_db));
+		local stmt_insert_fields = db.prepare(format("INSERT INTO %s__fields_metadata(name, type_id, length) VALUES(?,?,?)", attached_db));
+		foreach(idx, tbl in all_tables)
+		{
+			local is_view = tbl.type == "view" ? 1 : 0;
+			stmt_insert_table.bind(1, tbl.name);
+			stmt_insert_table.bind(2, is_view);
+			stmt_insert_table.step();
+			stmt_insert_table.reset();
+			
+			//if(!is_view)
+			{
+				local all_fields = exec_get_all(db, format("PRAGMA %stable_info(\"%s\");", attached_db, tbl.name));
+				foreach(fld in all_fields)
+				{
+					stmt_insert_fields.bind(1, fld.name);
+					local field_type = fld.type.gsub("%(.+$", "");
+					if(!field_type.len()) field_type = "VARCHAR";
+					local type_id = exec_get_one(db, format("SELECT id FROM %s__field_types WHERE name=?", attached_db), [field_type]);
+					stmt_insert_fields.bind(2, type_id);
+					local field_length = fld.type.match("%((%d+)%)");
+					stmt_insert_fields.bind(3, field_length);
+					stmt_insert_fields.step();
+					stmt_insert_fields.reset();
+				}
+			}
+		}
+		stmt_insert_table.finalize();
+		stmt_insert_fields.finalize();
+	}
+	
+	static function doUpdateTableMetadataFieldsFor(db, all_tables, attached_db="")
+	{
+		local stmt_insert_fields = db.prepare(format("INSERT INTO %s__table_metadata_fields(table_id, field_id) VALUES(?,?)", attached_db));
+		local stmt_insert_accept_fields = db.prepare(format("INSERT INTO %s__table_metadata_accept_fields(table_id, field_id) VALUES(?,?)", attached_db));
+		local stmt_insert_edit_links = db.prepare(format("INSERT INTO %s__table_metadata_edit_links(table_id, field_id, link_table_id, link_field_id, show_table_id, show_field_id, show_text_id) VALUES(?,?,?,?,?,?,?)",attached_db));
+		foreach(tbl in all_tables)
+		{
+			local all_fields = exec_get_all(db, format("PRAGMA %stable_info(\"%s\");", attached_db, tbl.name));
+			foreach(fld in all_fields)
+			{
+				stmt_insert_fields.bind(1, tbl.id);
+				local field_id = exec_get_one(db, format("SELECT id FROM %s__fields_metadata WHERE name=?", attached_db), [fld.name]);
+				stmt_insert_fields.bind(2, field_id); 
+				stmt_insert_fields.step();
+				stmt_insert_fields.reset();
+
+				local ro = getReadOnlyFields(db, attached_db);
+				if(!table_rawin(ro, fld.name))
+				{
+					stmt_insert_accept_fields.bind(1, tbl.id);
+					stmt_insert_accept_fields.bind(2, field_id); 
+					stmt_insert_accept_fields.step();
+					stmt_insert_accept_fields.reset();
+				}
+
+				if(fld.name.endswith("_id"))
+				{
+					field_id = db.last_row_id();
+					local table_name = fld.name.gsub("_id$", "s");
+					local link_table_id = exec_get_one(db, format("SELECT id FROM %s__tables_metadata WHERE name=?", attached_db), [table_name]);
+					local link_field_id = exec_get_one(db, format("SELECT id FROM %s__table_metadata_fields_view WHERE table_id_name=? AND field_id_name='id'", attached_db), [table_name]);
+					//print(table_name, link_table_id, link_field_id);
+					if(link_table_id && link_field_id)
+					{
+						stmt_insert_edit_links.bind(1, tbl.id);
+						stmt_insert_edit_links.bind(2,field_id);
+						stmt_insert_edit_links.bind(3, link_table_id);
+						stmt_insert_edit_links.bind(4, link_field_id);
+						stmt_insert_edit_links.bind(5, link_field_id);
+						stmt_insert_edit_links.bind(6, link_field_id);
+						stmt_insert_edit_links.bind(7, link_field_id);
+						stmt_insert_edit_links.step();
+						stmt_insert_edit_links.reset();
+					}
+				}
+			}			
+		}
+		stmt_insert_fields.finalize();
+		stmt_insert_accept_fields.finalize();
+		stmt_insert_edit_links.finalize();
+	}
+
+	static function doCleanTableMetadataFieldsFor(db, all_tables, attached_db="")
+	{
+		//cleanup references for inexisting tables/views at global level
+		local updateTablesMetadata = function()
+		{
+			local sql_exists = format("SELECT 1 FROM %ssqlite_master sm, %s__tables_metadata tm WHERE tm.id=a.%%s and sm.name=tm.name",
+				attached_db, attached_db);
+			local sql = format("UPDATE %s__tables_metadata a set list_table_id=NULL WHERE NOT EXISTS(%s)", attached_db, format(sql_exists, "list_table_id"));
+			db.exec_dml(sql);
+			sql = format("UPDATE %s__tables_metadata a set edit_table_id=NULL WHERE NOT EXISTS(%s)", attached_db, format(sql_exists, "edit_table_id"));
+			db.exec_dml(sql);
+			sql = format("UPDATE %s__app_menu a set table_view_id=NULL WHERE NOT EXISTS(%s)", attached_db, format(sql_exists, "table_view_id"));
+			db.exec_dml(sql);
+		}
+		updateTablesMetadata();
+
+		//now for each table clean references to inexistent fields/tables/views
+		foreach(tbl in all_tables)
+		{
+			local all_fields = exec_get_all(db, format("PRAGMA %stable_info(\"%s\");", attached_db, tbl.name));
+			local existing_fields = [];
+			foreach(fld in all_fields)
+			{
+				existing_fields.push(fld.name);
+			}
+			//if(!existing_fields.len()) continue;
+			existing_fields = "'" + existing_fields.concat("','") + "'";
+
+			local deleteNotIn = function(tbl_to_delete, tbl_id_name, field_names_to_keep, table_id_fld="table_id", field_id_fld="field_id")
+			{
+				local sql = format("DELETE FROM %s%s WHERE table_id IN (SELECT tm.id FROM %s__tables_metadata tm WHERE tm.name = '%s' AND NOT EXISTS(SELECT 1 FROM %ssqlite_master sm WHERE sm.name=tm.name))",
+					attached_db, tbl_to_delete, attached_db, tbl_id_name, attached_db);
+				db.exec_dml(sql);
+			
+				sql = format("DELETE FROM %s\"" + tbl_to_delete + "\" WHERE " + table_id_fld + 
+					"=(SELECT id FROM %s__tables_metadata WHERE name='" + 
+					tbl_id_name + "') AND " + field_id_fld + " NOT IN(SELECT id FROM %s__fields_metadata WHERE name IN(" + field_names_to_keep + "))",
+					attached_db, attached_db, attached_db);
+				//debug_print(sql, "\n");
+				db.exec_dml(sql);
+			}
+			//order is important here first any of __table_metadata_list_fields, __table_metadata_accept_fields, __table_metadata_edit_links,
+			//then __table_metadata_filter_tables and __table_metadata_fields
+			deleteNotIn("__table_metadata_list_fields", tbl.name, existing_fields);
+			deleteNotIn("__table_metadata_accept_fields", tbl.name, existing_fields);
+			
+			//__table_metadata_edit_links references tables/views in multiple fields
+			deleteNotIn("__table_metadata_edit_links", tbl.name, existing_fields, "show_table_id", "show_field_id");
+			deleteNotIn("__table_metadata_edit_links", tbl.name, existing_fields, "link_table_id", "link_field_id");
+			deleteNotIn("__table_metadata_edit_links", tbl.name, existing_fields);
+			
+			deleteNotIn("__table_metadata_filter_tables", tbl.name, existing_fields, "table_filtered_id", "table_filtered_field_id");
+			deleteNotIn("__table_metadata_fields", tbl.name, existing_fields);			
+		}
+
+		//finally remove inexistent tables/views
+		local sql = format("DELETE FROM %s__tables_metadata a WHERE NOT EXISTS(SELECT 1 FROM %ssqlite_master sm WHERE sm.name=a.name)",
+			attached_db, attached_db);
+		db.exec_dml(sql);
+	}
+
+	static function doUpdateMetadata(db, attached_db="")
+	{
+		db.exec_dml("BEGIN");
+
+		local all_tables = exec_get_all(db, format("SELECT type, name FROM %ssqlite_master WHERE type IN('view','table')", attached_db));
+		doUpdateTablesMetadataFor(db, all_tables);
+		
+		all_tables = exec_get_all(db, format("SELECT id, name FROM %s__tables_metadata", attached_db)); // WHERE is_view = 0");
+		doUpdateTableMetadataFieldsFor(db, all_tables);
+		
+		doCleanTableMetadataFieldsFor(db, all_tables);
+		
+		db.exec_dml("COMMIT");
+	}
+
+	static function getReferencesOnDBSchema(db, name, attached_db="")
+	{
+		local reference_name = escapeRE(sanitizeDBName(name));
+		local prefix_suffix_re = "[%s%(%),%.<>!=%-%+%*/\"']";
+		local reference_name_re = prefix_suffix_re + "(" + reference_name + ")" + prefix_suffix_re;
+		local reference_re = "()" + reference_name + "()";
+		//print(reference_re);
+
+		local checkValidRefenceName = function(whole_str, start_idx, end_idx)
+		{
+			//!!!this assume that the name searched is not at the begning or end of whole_str
+			local context = whole_str.slice( (start_idx ? start_idx-1 : start_idx) , 
+									(end_idx < whole_str.len() ? end_idx+1 : end_idx) );
+			if(context.match(reference_name_re))
+			{
+				if( (context[0] == '\'') && (context[context.len()-1] != '\'') ) return false;
+				else if( (context[0] == '"') && (context[context.len()-1] != '"') ) return false;
+				return true;
+			}
+			return false;
+		}
+
+		local result = blob(0, 8000);
+		local stmt = db.prepare(format("SELECT type, name, sql FROM %ssqlite_master;", attached_db));
+		while(stmt.next_row())
+		{
+			local sql = stmt.col(2);
+			if((::type(sql) == "string"))
+			{
+				sql = sql.tolower();
+				sql.gmatch(reference_re, function(start_idx, end_idx){
+						//print(start_idx, end_idx, sql.slice(start_idx, end_idx));
+						if(checkValidRefenceName(sql, start_idx, end_idx))
+						{
+							//print("idx", start_idx, end_idx, sql.slice(start_idx, end_idx));
+							//print(sql);
+							result.write("-------------\n");
+							result.write(stmt.col(0), "\t", stmt.col(1), "\n");
+							result.write(stmt.col(2), "\n");
+							return false; //one match is enough
+						}
+						return true;
+					});
+			}
+		}
+		stmt.finalize();
+
+		return result.tostring();
+	}
+
+	static function searchOnAllTables(db, search_str, search_limit, attached_db="")
+	{
+		local embedded_limit = search_str.match("^(%d+):");
+		if(embedded_limit)
+		{
+			search_limit = embedded_limit.tointeger();
+			search_str = search_str.match("^%d+:(.+)");
+		}
+		local the_search_str;
+		if( search_str.match("^_re_:") )
+		{
+			the_search_str = search_str.match("^_re_:(.+)");
+		}
+		else the_search_str = escapeRE(search_str);
+		the_search_str = the_search_str.tolower();
+		local search_count = 0;
+		local result = blob(0, 8000);
+		local stmt = db.prepare(format("SELECT name FROM %ssqlite_master WHERE type='table';", attached_db));
+		while(stmt.next_row())
+		{
+			local tbl_name = stmt.col(0);
+			local tbl_stmt = db.prepare(format("SELECT * FROM %s\"%s\"", attached_db, tbl_name));
+			local col_count = tbl_stmt.col_count();
+			local text_cols = [];
+			for(local i=0; i < col_count; ++i)
+			{
+				local dtype = tbl_stmt.col_declared_type(i).tolower();
+				if( (dtype.indexOf("varchar") >= 0) || (dtype.indexOf("text") >= 0) ) text_cols.push(i);
+			}
+			if(text_cols.len())
+			{
+				local text_cols_len = text_cols.len();
+				local tbl_done = false;
+				while(tbl_stmt.next_row() && !tbl_done)
+				{
+					for(local i=0; i < text_cols_len; ++i)
+					{
+						local col_idx = text_cols[i];
+						local str = tbl_stmt.col(col_idx);
+						if((::type(str) == "string"))
+						{
+							if(str.tolower().match(the_search_str))
+							{
+								result.write(tbl_name, ":", tbl_stmt.col_name(col_idx), "\n");
+								++search_count;
+								tbl_done = true;
+								break;
+							}
+						}
+					}
+				}
+			}
+			tbl_stmt.finalize();
+			
+			if(search_count >= search_limit) break;
+		}
+		stmt.finalize();
+
+		return result.tostring();
+	}
+	
+}
+
+class SqlPreprocessor
+{
+	stmt_get = null;
+	stmt_get_by_name = null;
+	stmt_set = null;
+	
+	function createSqlPreprocessorMainTable(db)
+	{
+		local sql = [==[
+CREATE TABLE IF NOT EXISTS __app_sql (
+	id INTEGER PRIMARY KEY NOT NULL,
+	_version_ INTEGER NOT NULL DEFAULT 0,
+	_cdate_ DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+	_mdate_ DATETIME,
+	name VARCHAR NOT NULL COLLATE NOCASE
+	CONSTRAINT __app_sql_name_unique UNIQUE,
+	base TEXT COLLATE NOCASE NOT NULL,
+	sql TEXT COLLATE NOCASE,
+	notes TEXT COLLATE NOCASE
+);		
+]==];
+		db.exec_dml(sql);
+	}
+	
+	function doPrepare(db)
+	{
+		if(!stmt_get)
+		{
+			stmt_get = db.prepare("SELECT base FROM __app_sql WHERE id=?");
+			stmt_get_by_name = db.prepare("SELECT base FROM __app_sql WHERE name=?");
+			stmt_set = db.prepare("UPDATE __app_sql SET sql=? WHERE id=?");
+		}
+	}
+	
+	function doFinalize(db)
+	{
+		if(stmt_get)
+		{
+			stmt_get.finalize();
+			stmt_get = null;
+			stmt_set.finalize();
+			stmt_get_by_name.finalize();
+		}
+	}
+	
+	//the macros for params are {$param_name:param_value}
+	static function preprocessSqlQueryParams(sql_seed, params)
+	{
+		local found;
+		do {
+			found = false;
+			sql_seed = sql_seed.gsub("(%b{})", function(m){
+				if(m[1] == '$')
+				{
+					found = true;
+					local ary = m.slice(2,-1).split(':');
+					local val = table_get(params, ary[0], ary[1]);
+					return val;
+				}
+				return m;
+			});
+			//print("loop", found);
+		} while(found);
+		
+		return sql_seed;
+	}	
+
+	function preprocessSqlQuery(db, sql_seed, id)
+	{
+		local need_prepare = stmt_get == null;
+		if(need_prepare) doPrepare(db);
+				
+		if(sql_seed)
+		{
+			local found;
+			local self = this;
+			do {
+				found = false;
+				sql_seed = sql_seed.gsub("(%b{})", function(m){
+					if(m[1] == ':')
+					{
+						local vt = m.slice(2,-1);
+						self.stmt_get_by_name.bind(1, vt);
+						if(self.stmt_get_by_name.next_row())
+						{
+							//need check to prevent infinite recursion
+							found = true;
+							local val = self.stmt_get_by_name.col(0);
+							self.stmt_get_by_name.reset();
+							return val;
+						}
+					}
+					return m;
+				});
+				//print("loop", found);
+			} while(found);
+			stmt_set.bind(1, sql_seed);
+			stmt_set.bind(2, id);
+			stmt_set.step();
+			stmt_set.reset();
+		}
+		if(need_prepare) doFinalize(db);
+		return sql_seed;
+	}
+
+	//the macros for params are {:query_name_to_include}
+	function preprocessSqlQueries(db)
+	{
+		local stmt = db.prepare("SELECT id FROM __app_sql");
+		local all_ids = [];
+		while(stmt.next_row())
+		{
+			all_ids.push(stmt.col(0));
+		}
+		stmt.finalize();
+		
+		doPrepare(db);
+
+		try
+		{
+			db.exec_dml("BEGIN");
+			foreach(id in all_ids)
+			{
+				local found;
+				local sql_seed = false;
+				stmt_get.bind(1, id);
+				if(stmt_get.next_row()) sql_seed = stmt_get.col(0);
+				stmt_get.reset();
+				
+				if(sql_seed)
+				{
+					preprocessSqlQuery(db, sql_seed, id);
+				}
+			}
+			db.exec_dml("COMMIT");
+		}
+		catch(e)
+		{
+			db.exec_dml("ROLLBACK");
+		}
+		doFinalize(db);
+	}
+	
+	function getPreprocessorQueryName(db, sql)
+	{
+		createSqlPreprocessorMainTable(db);
+		return sql.match("^%-%-name=(%S+)");
+	}
+	
+	function getPreprocessorQuery(db, sql, field="ifnull(sql, base) as sql")
+	{
+		local name = getPreprocessorQueryName(db, sql);
+		if(name)
+		{
+			local stmt = db.prepare(format("SELECT %s FROM __app_sql WHERE name=?", field));
+			stmt.bind(1, name);
+			if(stmt.next_row())
+			{
+				name = stmt.col(0);
+			}
+			stmt.finalize();
+		}
+		return name;
+	}
+
+	function insertPreprocessorQuery(db, sql)
+	{
+		local name = getPreprocessorQueryName(db, sql);
+		if(name)
+		{
+			local stmt = db.prepare("insert into __app_sql(name, base) values(?,?)");
+			stmt.bind(1, name);
+			stmt.bind(2, sql);
+			stmt.step();
+			print(db.errmsg());
+			stmt.finalize();
+			preprocessSqlQueries(db);
+		}
+		return name;
+	}
+
+	function updatePreprocessorQuery(db, sql)
+	{
+		local name = getPreprocessorQueryName(db, sql);
+		if(name)
+		{
+			local stmt = db.prepare("update __app_sql set base = ? where name = ?");
+			stmt.bind(1, sql);
+			stmt.bind(2, name);
+			stmt.step();
+			stmt.finalize();
+			preprocessSqlQueries(db);
+		}
+		return name;
+	}
+}

+ 50 - 6
SquiLu-ourbiz/sqlite3-cc-gui.fl

@@ -38,6 +38,11 @@ widget_class Sqlite3cc_Window {
         label {Close DB}
         xywh {0 0 30 20} shortcut 0x40077 labelsize 16 divider
       }
+      MenuItem menu_file_open_csv {
+        label {Open CSV}
+        dirty_name menu_file_open_csv
+        xywh {0 0 36 21} divider
+      }
       MenuItem menu_file_execute {
         label Execute
         xywh {0 0 30 20} shortcut 0x40065 labelsize 16 divider
@@ -104,10 +109,19 @@ widget_class Sqlite3cc_Window {
         xywh {5 25 160 255} labelsize 16 textsize 16 resizable
         class Fl_Data_Table
       }
-      Fl_Input iTablesFilter {
-        label {Filter:}
-        dirty_name iTablesFilter
-        xywh {5 280 160 25} labeltype NO_LABEL when 1
+      Fl_Group {} {open
+        xywh {5 280 160 25}
+      } {
+        Fl_Input iTablesFilter {
+          label {Filter:}
+          dirty_name iTablesFilter
+          xywh {5 280 140 25} labeltype NO_LABEL when 1 resizable
+        }
+        Fl_Check_Button chkSytemTables {
+          label {System tables}
+          dirty_name chkSytemTables
+          xywh {147 281 18 24} down_box DOWN_BOX labeltype NO_LABEL
+        }
       }
     }
     Fl_Group {} {open
@@ -120,7 +134,7 @@ widget_class Sqlite3cc_Window {
           xywh {165 25 615 35} box DOWN_FRAME labelsize 16
         } {
           Fl_Input iMaxRows {
-            label {Max Rows}
+            label {Max Rows} selected
             xywh {265 30 49 25} type Int labelsize 16 textsize 16
             code0 {o->value("50");}
           }
@@ -155,7 +169,7 @@ widget_class Sqlite3cc_Window {
             }
             MenuItem menu_sql_schema_update_norefs {
               label {schema update norefs}
-              dirty_name menu_sql_schema_update_norefs selected
+              dirty_name menu_sql_schema_update_norefs
               xywh {10 10 36 21}
             }
             MenuItem menu_sql_sqlite_master_update {
@@ -193,6 +207,36 @@ widget_class Sqlite3cc_Window {
               dirty_name menu_sql_search_all_tables
               xywh {0 0 36 21}
             }
+            MenuItem menu_sql_macros {
+              label {sql macros}
+              dirty_name menu_sql_macros
+              xywh {10 10 36 21}
+            }
+            MenuItem menu_sql_macros_base {
+              label {sql macros base}
+              dirty_name menu_sql_macros_base
+              xywh {0 0 36 21}
+            }
+            MenuItem menu_sql_macros_sql {
+              label {sql macros sql}
+              dirty_name menu_sql_macros_sql
+              xywh {0 0 36 21}
+            }
+            MenuItem menu_sql_macros_insert {
+              label {sql macros insert}
+              dirty_name menu_sql_macros_insert
+              xywh {0 0 36 21}
+            }
+            MenuItem menu_sql_macros_update {
+              label {sql macros update}
+              dirty_name menu_sql_macros_update
+              xywh {0 0 36 21}
+            }
+            MenuItem menu_sql_update_metadata {
+              label {sql update metadata}
+              dirty_name menu_sql_update_metadata
+              xywh {0 0 36 21}
+            }
           }
           Fl_Button btnCreateQuery {
             label {@->}

+ 32 - 4
SquiLu-ourbiz/sqlite3-cc-gui.nut

@@ -7,6 +7,7 @@ class Sqlite3cc_Window extends Fl_Double_Window {
   menu_file_reopen : Submenu;
   menu_file_attach : Submenu;
   menu_file_close : Submenu;
+  menu_file_open_csv : Submenu;
   menu_file_execute : Submenu;
   menu_file_exit : Submenu;
   menu_settings_encoding : Submenu;
@@ -17,6 +18,7 @@ class Sqlite3cc_Window extends Fl_Double_Window {
   group_db_tables : Fl_Group;
   grid_tables : Fl_Data_Table;
   iTablesFilter : Fl_Input;
+  chkSytemTables : Fl_Check_Button;
   group_sql : Fl_Group;
   group_buttons : Fl_Group;
   iMaxRows : Fl_Input;
@@ -34,6 +36,12 @@ class Sqlite3cc_Window extends Fl_Double_Window {
   menu_sql_create_trigger : MenuItem;
   menu_sql_references : MenuItem;
   menu_sql_search_all_tables : MenuItem;
+  menu_sql_macros : MenuItem;
+  menu_sql_macros_base : MenuItem;
+  menu_sql_macros_sql : MenuItem;
+  menu_sql_macros_insert : MenuItem;
+  menu_sql_macros_update : MenuItem;
+  menu_sql_update_metadata : MenuItem;
   btnCreateQuery : Fl_Button;
   btnExecute : Fl_Button;
   btnLoad : Fl_Button;
@@ -86,6 +94,7 @@ class Sqlite3cc_Window extends Fl_Double_Window {
         menu_file_reopen = o.add(_tr("File/Reopen DB"), 0);
         menu_file_attach = o.add(_tr("File/Attach DB"), 0);
         menu_file_close = o.add(_tr("File/Close DB"), 0, null, null, 128);
+        menu_file_open_csv = o.add(_tr("File/Open CSV"), 0, null, null, 128);
         menu_file_execute = o.add(_tr("File/Execute"), 0, null, null, 128);
         menu_file_exit = o.add(_tr("File/Exit"), 0);
         //menu_settings Settings
@@ -118,10 +127,23 @@ class Sqlite3cc_Window extends Fl_Double_Window {
               Fl_Group.current().resizable(o);
             }
             {
-              local o = Fl_Input(5, 280, 160, 25, _tr("Filter:"));
-              iTablesFilter = o;
-              o.labeltype(FL_NO_LABEL);
-              o.when(1);
+              local o = Fl_Group(5, 280, 160, 25);
+              {
+                {
+                  local o = Fl_Input(5, 280, 140, 25, _tr("Filter:"));
+                  iTablesFilter = o;
+                  o.labeltype(FL_NO_LABEL);
+                  o.when(1);
+                  Fl_Group.current().resizable(o);
+                }
+                {
+                  local o = Fl_Check_Button(147, 281, 18, 24, _tr("System tables"));
+                  chkSytemTables = o;
+                  o.labeltype(FL_NO_LABEL);
+                  o.down_box(FL_DOWN_BOX);
+                }
+              }
+              o.end();
             }
           }
           o.end();
@@ -169,6 +191,12 @@ class Sqlite3cc_Window extends Fl_Double_Window {
                         menu_sql_create_trigger = o.add(_tr("create trigger"), 0);
                         menu_sql_references = o.add(_tr("references"), 0);
                         menu_sql_search_all_tables = o.add(_tr("search all tables"), 0);
+                        menu_sql_macros = o.add(_tr("sql macros"), 0);
+                        menu_sql_macros_base = o.add(_tr("sql macros base"), 0);
+                        menu_sql_macros_sql = o.add(_tr("sql macros sql"), 0);
+                        menu_sql_macros_insert = o.add(_tr("sql macros insert"), 0);
+                        menu_sql_macros_update = o.add(_tr("sql macros update"), 0);
+                        menu_sql_update_metadata = o.add(_tr("sql update metadata"), 0);
                       }
                     }
                     {

+ 206 - 438
SquiLu-ourbiz/sqlite3-cc.nut

@@ -5,6 +5,8 @@
  * Licensed under GPLv3, see http://www.gnu.org/licenses/gpl.html.
  */
  
+dofile("sqlite-utils.nut");
+ 
 function _tr(str) {return str;}
 
 class Fl_Multiline_Output extends Fl_Output {
@@ -280,7 +282,7 @@ class Fl_Data_Table extends Flv_Data_Table {
 	function clear_selection(){
 	}
 	function get_col_name(idx){
-		return _cols_info[idx].colname;
+		return _cols_info.len() ? _cols_info[idx].colname : idx.tostring();
 	}
 	function get_row(arow=null){
 		if(arow == null) arow = row();
@@ -323,174 +325,18 @@ class Fl_Data_Table extends Flv_Data_Table {
 
 dofile("sqlite3-cc-gui.nut", false, false);
 
-function create_stmt_bind(db, sql, bind_values=null)
-{
-	local result = false;
-	local stmt = db.prepare(sql);
-	if(bind_values)
-	{
-		foreach(k,v in bind_values)
-		{
-			stmt.bind(k+1, v);
-		}
-	}
-	return stmt;
-}
-
-function exec_get_all(db, sql, bind_values=null)
-{
-	local stmt = create_stmt_bind(db, sql, bind_values);
-	local result = stmt.asArrayOfTables();
-	stmt.finalize();
-	return result;
-}
-
-function exec_get_one(db, sql, bind_values=null)
-{
-	local result = null;
-	local stmt = create_stmt_bind(db, sql, bind_values);
-	if(stmt.next_row())
-	{
-		result = stmt.col(0);
-	}
-	stmt.finalize();
-	return result;
-}
-
-function exec_dml(db, sql, bind_values=null)
-{
-	local stmt = create_stmt_bind(db, sql, bind_values);
-	local result = stmt.step() == stmt.SQLITE_DONE;
-	stmt.finalize();
-	return result;
-}
-
-local function sanitizeDBName(dbname)
-{
-	return dbname.gsub("([^_%-a-zA-Z0-9])","");
-}
-
-local function escapeRE(str)
-{
-	return str.gsub("(%-)", "%%%1");
-}
-
-function getReferencesOnDBSchema(db, name)
-{
-	local reference_name = escapeRE(sanitizeDBName(name));
-	local prefix_suffix_re = "[%s%(%),%.<>!=%-%+%*/\"']";
-	local reference_name_re = prefix_suffix_re + "(" + reference_name + ")" + prefix_suffix_re;
-	local reference_re = "()" + reference_name + "()";
-	//print(reference_re);
-
-	local checkValidRefenceName = function(whole_str, start_idx, end_idx)
-	{
-		//!!!this assume that the name searched is not at the begning or end of whole_str
-		local context = whole_str.slice( (start_idx ? start_idx-1 : start_idx) , 
-								(end_idx < whole_str.len() ? end_idx+1 : end_idx) );
-		if(context.match(reference_name_re))
-		{
-			if( (context[0] == '\'') && (context[context.len()-1] != '\'') ) return false;
-			else if( (context[0] == '"') && (context[context.len()-1] != '"') ) return false;
-			return true;
-		}
-		return false;
-	}
-
-	local result = blob(0, 8192);
-	local stmt = db.prepare("SELECT type, name, sql FROM sqlite_master;");
-	while(stmt.next_row())
-	{
-		local sql = stmt.col(2);
-		if((::type(sql) == "string"))
-		{
-			sql = sql.tolower();
-			sql.gmatch(reference_re, function(start_idx, end_idx){
-					//print(start_idx, end_idx, sql.slice(start_idx, end_idx));
-					if(checkValidRefenceName(sql, start_idx, end_idx))
-					{
-						//print("idx", start_idx, end_idx, sql.slice(start_idx, end_idx));
-						//print(sql);
-						result.write("-------------\n");
-						result.write(stmt.col(0), "\t", stmt.col(1), "\n");
-						result.write(stmt.col(2), "\n");
-						return false; //one match is enough
-					}
-					return true;
-				});
-		}
-	}
-	stmt.finalize();
-
-	return result.tostring();
-}
-
-local function searchOnAllTables(db, search_str, search_limit)
-{
-	local embedded_limit = search_str.match("^(%d+):");
-	if(embedded_limit)
-	{
-		search_limit = embedded_limit.tointeger();
-		search_str = search_str.match("^%d+:(.+)");
-	}
-	local the_search_str;
-	if( search_str.match("^_re_:") )
-	{
-		the_search_str = search_str.match("^_re_:(.+)");
-	}
-	else the_search_str = escapeRE(search_str);
-	the_search_str = the_search_str.tolower();
-	local search_count = 0;
-	local result = blob(0, 8192);
-	local stmt = db.prepare("SELECT name FROM sqlite_master WHERE type='table';");
-	while(stmt.next_row())
-	{
-		local tbl_name = stmt.col(0);
-		local tbl_stmt = db.prepare("SELECT * FROM \"" + tbl_name + "\"");
-		local col_count = tbl_stmt.col_count();
-		local text_cols = [];
-		for(local i=0; i < col_count; ++i)
-		{
-			local dtype = tbl_stmt.col_declared_type(i).tolower();
-			if( (dtype.indexOf("varchar") >= 0) || (dtype.indexOf("text") >= 0) ) text_cols.push(i);
-		}
-		if(text_cols.len())
-		{
-			local text_cols_len = text_cols.len();
-			local tbl_done = false;
-			while(tbl_stmt.next_row() && !tbl_done)
-			{
-				for(local i=0; i < text_cols_len; ++i)
-				{
-					local col_idx = text_cols[i];
-					local str = tbl_stmt.col(col_idx);
-					if((::type(str) == "string"))
-					{
-						if(str.tolower().match(the_search_str))
-						{
-							result.write(tbl_name, ":", tbl_stmt.col_name(col_idx), "\n");
-							++search_count;
-							tbl_done = true;
-							break;
-						}
-					}
-				}
-			}
-		}
-		tbl_stmt.finalize();
-		
-		if(search_count >= search_limit) break;
-	}
-	stmt.finalize();
-
-	return result.tostring();
-}
-
 local function multiply3(ctx,a,b,c){
 	//print(ctx.user_data());
 	ctx.result_double(a*b*c);
 }
 
+local function sqlite3_progress_handler(window)
+{
+	//print("sqlite3_progress_handler");
+	Fl.check();
+	return window.ref()._stop_processing_query ? 1 : 0;
+}
+
 class Sqlite3CC extends Sqlite3cc_Window {
   
 	// Declaration of class members
@@ -501,6 +347,11 @@ class Sqlite3CC extends Sqlite3cc_Window {
 	db = null;
 	attached_databases = null;
 	squilu_edit_window = null;
+	_busy_working = null;
+	_stop_processing_query = null;
+	//_history_db_name = null;
+	//_history_db = null;
+	_hide_system_tables = false;
   
 	constructor(){
 		base.constructor();
@@ -510,11 +361,27 @@ class Sqlite3CC extends Sqlite3cc_Window {
 		tabView.callback(tabView_cb);
 		grid_tables._call_this = this.weakref();
 		grid_data._call_this = this.weakref();
+		grid_fields._call_this = this.weakref();
+		gridIndexes._call_this = this.weakref();
+		gridTriggers._call_this = this.weakref();
 		local last_db_file_name = Fl.preferences_get("last_db_file_name", "");
 		openDB(last_db_file_name);
 		iTablesFilter.callback(doDataSearch_cb);
+		chkSytemTables.callback(showHideSystemTables_cb);
 	}
 	
+	function setBusyWorking(turnOn)
+	{
+		if(turnOn && _busy_working)
+		{
+			_stop_processing_query = true;
+			return false;
+		}
+		_busy_working = turnOn;
+		_stop_processing_query = false;
+		return true;
+	}
+
 	function doDataSearch_cb(sender : Fl_Widget, udata : any)
 	{
 		this = sender->window();
@@ -522,27 +389,58 @@ class Sqlite3CC extends Sqlite3cc_Window {
 		grid_tables->set_data(filtered_data);
 	}
 	
+	function filterSystemTables()
+	{
+		local the_data = _my_tables_data;
+		local filtered_data = [];
+		foreach(rec in the_data)
+		{
+			local tbl_name = rec[1];
+			if(_hide_system_tables && tbl_name.len() && tbl_name.startswith("__")) continue;
+			filtered_data.push(rec);
+		}
+		return filtered_data
+	}
+	
+	function showHideSystemTables()
+	{
+		_hide_system_tables = chkSytemTables->value();
+		local filtered_data = filterSystemTables();
+		grid_tables->set_data(filtered_data);
+	}
+	
+	function showHideSystemTables_cb(sender : Fl_Widget, udata : any)
+	{
+		this = sender->window();
+		showHideSystemTables();
+	}
+	
 	function doFilterMyData(value, prev_filtered_data)
 	{
 		local the_data = prev_filtered_data;
 		local value_len = value.len();
 		//print(__LINE__, _my_tables_data.len(), the_data.len(), value.len(), value);
-		if(value_len == 0) return _my_tables_data;
+		if(value_len == 0) return _hide_system_tables ? filterSystemTables() : _my_tables_data;
 		
 		if(value_len == 1 || the_data.len() == 0)
 		{
 			//we start from scratch
-			the_data = _my_tables_data;
+			return _hide_system_tables ? filterSystemTables() : _my_tables_data;
 		}
 		local filtered_data = [];
 		foreach(rec in the_data)
 		{
+			if(_hide_system_tables)
+			{
+				local tbl_name = rec[1];
+				if(tbl_name.len() && tbl_name.startswith("__")) continue;
+			}
 			foreach(field in rec)
 			{
 				if(field)
 				{
 					local fs = field.tostring();
-					if(fs.len() && fs.tostring().indexOf(value) >= 0)
+					if(fs.len() && fs.indexOf(value) >= 0)
 					{
 						filtered_data.push(rec);
 						break;
@@ -553,63 +451,26 @@ class Sqlite3CC extends Sqlite3cc_Window {
 		return filtered_data;
 	}
 	
-	function getSchemaVersion()
+	function getGridTableAttachedDB()
 	{
-		return db.exec_get_one("PRAGMA schema_version");
+		return (grid_tables.cols() > 3) ? (grid_tables.get_value(grid_tables.row(), 3) + ".") : "";
 	}
 	
 	function checkSchemaVersion()
 	{
-		if(_the_schema_version != getSchemaVersion())
+		if(_the_schema_version != SQLiteUtils.getSchemaVersion(db, getGridTableAttachedDB()))
 		{
 			local tables_idx = grid_tables->row();
-			get_tables();
+			get_tables(false);
 			local v = iTablesFilter->value();
 			if(v.len())
 			{
 				doDataSearch_cb(iTablesFilter, null);
 			}
+			else showHideSystemTables();
 			grid_tables->row(tables_idx < grid_tables->rows() ? tables_idx : grid_tables->rows()-1);
 		}
 	}
-
-	function getSchemaSqlFor(tbl)
-	{
-		local str = format("SELECT sql FROM sqlite_master WHERE (type='table' OR type='view') AND tbl_name='%s'", tbl);
-		return str;
-	}
-	function getSchemaFor(tbl)
-	{
-		local str = db.exec_get_one(getSchemaSqlFor(tbl));
-		return str;
-	}
-	
-	function getIndexesSqlFor(tbl)
-	{
-		local str = format("SELECT name, sql FROM sqlite_master WHERE type='index' AND tbl_name='%s' ORDER BY name;", tbl);
-		return str;
-	}
-	
-	function getTriggersSqlFor(tbl)
-	{
-		local str = format("SELECT name, sql FROM sqlite_master WHERE type='trigger' AND tbl_name='%s' ORDER BY name;", tbl);
-		return str;
-	}
-	
-	function getFieldsFor(tbl)
-	{
-		local stmt = db.prepare("select * from \"" + tbl + "\"");
-		local fields = stmt.colsAsArray();
-		stmt.finalize();
-		return fields;
-	}
-
-	function getFieldsAsCSVFor(tbl)
-	{
-		local fields = getFieldsFor(tbl);
-		local str_fields =  "\"" + fields.concat("\", \"") + "\"";
-		return str_fields;
-	}
 	
 	function showExecutionTime(start_time)
 	{
@@ -622,15 +483,26 @@ class Sqlite3CC extends Sqlite3cc_Window {
 	{
 		if(sender == grid_tables){
 			local tbl = grid_tables.get_value(grid_tables.row(), 1);
-			refreshTabView(tbl); 
+			refreshTabView(tbl, getGridTableAttachedDB()); 
 		}
 	}
 	
+	function showRecord(the_grid)
+	{
+		local row = the_grid.get_row();
+		local record = blob(0, 8000);
+		foreach(idx, val in row)
+			record.write((idx ? "\n" : ""), "----", idx, ":", 
+				grid_data.get_col_name(idx),"\n", val);
+		edit_record.value(record.tostring());
+		tabView->value(groupRecord);
+	}
+	
 	function row_selected(sender, ev){
 		if(sender == grid_tables){
 			if(ev == Fl_Data_Table_Events.e_update){
 				local tbl = grid_tables.get_value(grid_tables.row(), 1);
-				local sql = "select * from \"" + tbl + "\"";
+				local sql = format("select * from %s\"%s\"", getGridTableAttachedDB(), tbl);
 				local limit = iMaxRows.value();
 				if(limit && limit.len()) sql += " limit " + limit;
 				local start_time = os.clock();
@@ -639,43 +511,47 @@ class Sqlite3CC extends Sqlite3cc_Window {
 				tabView->value(groupData);
 			}
 		}
-		else if(sender == grid_data){
-			if(ev == Fl_Data_Table_Events.e_update){
-				local row = grid_data.get_row();
-				local record = blob(0, 8192);
-				foreach(idx, val in row)
-					record.write((idx ? "\n" : ""), "----", idx, ":", 
-						grid_data.get_col_name(idx),"\n", val);
-				edit_record.value(record.tostring());
-				tabView->value(groupRecord);
-			}
+		else if(
+			(sender == grid_fields) || 
+			(sender == grid_data) || 
+			(sender == gridIndexes) || 
+			(sender == gridTriggers)
+			){
+				if(ev == Fl_Data_Table_Events.e_update){
+					showRecord(sender);
+				}
 		}
 	}
   
 	function btnExecute_cb(sender, udata){
 		this = sender->window();
-		local sql = edit_queries->value();
-		if(sql && sql.len()) {
+		if(!setBusyWorking(true)) return;
+		local sql;
+		if(edit_queries->buffer()->selected()) sql = edit_queries->buffer()->selection_text();
+		else sql = edit_queries->value();
+		
+		local action = option_query.text();
+		if( (sql && sql.len()) || (action == "sql update metadata")) {
 			local start_time;
-			local action = option_query.text();
 			local foreign_keys_saved = null;
+			local cursor_wait = fl_cursor_wait();
 			switch(action)
 			{
 				case "references":
-					local references = getReferencesOnDBSchema(db, sql);
+					local references = SQLiteUtils.getReferencesOnDBSchema(db, sql, getGridTableAttachedDB());
 					edit_references->value(references);
 					tabView->value(groupReferences);
 				break;
 
 				case "search all tables":
-					local references = searchOnAllTables(db, sql, iMaxRows->value().tointeger());
+					local references = SQLiteUtils.searchOnAllTables(db, sql, iMaxRows->value().tointeger(), getGridTableAttachedDB());
 					edit_references->value(references);
 					tabView->value(groupReferences);
 				break;
 
 				case "schema update":
 				case "schema update norefs":
-					foreign_keys_saved = db.exec_get_one("PRAGMA foreign_keys");
+					foreign_keys_saved = db.exec_get_one(format("PRAGMA %sforeign_keys", getGridTableAttachedDB()));
 				case "insert":
 				case "update":
 				case "delete":
@@ -694,11 +570,12 @@ class Sqlite3CC extends Sqlite3cc_Window {
 
 						switch(action)
 						{
+							case "sqlite_master update":
+								_the_schema_version = 0;
 							case "create index":
 							case "create trigger":
 							case "schema update":
 							case "schema update norefs":
-							case "sqlite_master update":
 							case "drop table":
 								checkSchemaVersion();
 							break;
@@ -706,8 +583,8 @@ class Sqlite3CC extends Sqlite3cc_Window {
 					}
 					catch(e)
 					{
-						if(!db.IsAutoCommitOn() || (action == "schema update")) db.exec_dml("ROLLBACK;");
-						if(foreign_keys_saved != null) db.exec_dml("PRAGMA foreign_keys=" + foreign_keys_saved);
+						if(!db.IsAutoCommitOn() || action.startswith("schema update")) db.exec_dml("ROLLBACK;");
+						if(foreign_keys_saved != null) db.exec_dml(format("PRAGMA %sforeign_keys=" + foreign_keys_saved, getGridTableAttachedDB()));
 						fl_alert(e);
 					}
 				break;
@@ -715,11 +592,46 @@ class Sqlite3CC extends Sqlite3cc_Window {
 				case "dump table":
 				break;
 
+				case "sql update metadata":
+					start_time = os.clock();
+					SQLiteUtils.doUpdateMetadata(db);
+					showExecutionTime(start_time);
+					checkSchemaVersion();
+				break;
+
+				case "sql macros base":
+					local sql_pp = new SqlPreprocessor();
+					local new_sql = sql_pp.getPreprocessorQuery(db, sql, "base");
+					if(new_sql) edit_queries->value(new_sql);
+				break;
+				case "sql macros sql":
+					local sql_pp = new SqlPreprocessor();
+					local new_sql = sql_pp.getPreprocessorQuery(db, sql, "sql");
+					if(new_sql) edit_queries->value(new_sql);
+				break;
+				case "sql macros insert":
+					local sql_pp = new SqlPreprocessor();
+					sql_pp.insertPreprocessorQuery(db, sql);
+				break;
+				case "sql macros update":
+					local sql_pp = new SqlPreprocessor();
+					sql_pp.updatePreprocessorQuery(db, sql);
+				break;
+				case "sql macros":
+					local sql_pp = new SqlPreprocessor();
+					local new_sql = sql_pp.getPreprocessorQuery(db, sql);
+					if(new_sql)
+					{
+						sql = sql_pp.preprocessSqlQueryParams(new_sql, {});
+						edit_queries->value(sql);
+					}
+					else break;
+
 				default:
 					try
 					{
 						start_time = os.clock();
-						get_records_by_sql(grid_data, sql, true);
+						get_records_by_sql(grid_data, sql, true, false);
 						showExecutionTime(start_time);
 						tabView->value(groupData);
 						checkSchemaVersion();
@@ -731,233 +643,41 @@ class Sqlite3CC extends Sqlite3cc_Window {
 					}
 			}
 		}
-	}
-	
-	function getIndexesAndTriggersFor(tbl, result)
-	{
-		local stmt = db.prepare(getIndexesSqlFor(tbl));
-		while(stmt.next_row())
-		{
-			local str = stmt.col(1);
-			if(::type(str) == "string") result.write("\n\n", str, ";");
-		}
-		stmt.finalize();
-
-		stmt = db.prepare(getTriggersSqlFor(tbl));
-		while(stmt.next_row())
-		{
-			local str = stmt.col(1);
-			if(::type(str) == "string") result.write("\n\n", str, ";");
-		}
-		stmt.finalize();
+		setBusyWorking(false);
 	}
   
 	function btnCreateQuery_cb(sender, udata){
 		this = sender->window();
 		if(db){
 			local tbl = grid_tables.get_value(grid_tables.row(), 1);
-			local sql = option_query.text();
-			local fields = getFieldsFor(tbl);
-			local fields_csv = "\"" + fields.concat("\", \"") + "\"";
-			
-			local genSchemaUpgrade = function(with_references)
-			{
-				local new_suffix = "___new";
-				local old_suffix = "";
-				local result = blob(0, 8192);
-				result.write("PRAGMA foreign_keys=OFF;\n\nBEGIN;\n\n");
-				local str_schema = getSchemaFor(tbl);
-				str_schema = str_schema.gsub("(" + escapeRE(tbl) + ")", "%1" + new_suffix, 1);
-				str_schema = str_schema.gsub("\n%s+", "\n\t");
-				result.write(str_schema, ";");
-				fields_csv = fields_csv.gsub(", ", ",\n\t");
-				result.write(format("\n\nINSERT INTO %s%s(\n\t%s\n\t)\nSELECT\n\t%s\nFROM \"%s%s\";", tbl, new_suffix, fields_csv, fields_csv, tbl, old_suffix));
-				result.write("\n\nDROP TABLE \"", tbl, old_suffix, "\";");
-				result.write("\n\nALTER TABLE \"", tbl, new_suffix, "\" RENAME TO \"", tbl, old_suffix, "\";");
-				
-				getIndexesAndTriggersFor(tbl, result);
-				
-				if(with_references)
-				{
-					result.write("\n\nDROP VIEW \"view_name\";\n\n");
-					result.write(getReferencesOnDBSchema(db, tbl));
-					result.write("\n\nCREATE VIEW  \"view_name\"  AS \"db_table_name\";");
-				}
-				result.write("\n\nPRAGMA foreign_key_check;\n\nCOMMIT;\n\nPRAGMA foreign_keys=ON;");
-				return result.tostring();
-			};
-			
-			if(sql == "select")
-			{
-				local alias_letter = 'a';
-				local myjoins = "";
-				local fields_last_idx = fields.len()-1;
-
-				local stmt = db.prepare(format("PRAGMA foreign_key_list(\"%s\")", tbl));
-				local last_fk_id = -1;
-				while(stmt.next_row())
-				{
-					local fk_id = stmt.col(0);
-					local ftable = stmt.col(2);
-					local ffrom = stmt.col(3);
-					local fto = stmt.col(4);
-					local field_idx = fields.find(ffrom);
-					local is_new_join = last_fk_id != fk_id;
-					if(is_new_join) ++alias_letter;
-					if(field_idx)
-					{
-						fields[field_idx] += format("\"%s --%c.\"%s", (fields_last_idx == field_idx ? "" : ","), alias_letter, fto);
-					}
-					if(is_new_join)
-					{
-						myjoins += format("\n--LEFT JOIN \"%s\" AS %c ON a.\"%s\" = %c.\"%s\"", ftable, alias_letter, ffrom, alias_letter, fto);
-					}
-					else
-					{
-						myjoins += format(" AND a.\"%s\" = %c.\"%s\"", ffrom, alias_letter, fto);
-					}
-					last_fk_id = fk_id;
-				}
-				stmt.finalize();
-
-				fields_csv = "a.\"" + fields.concat("\",\n\ta.\"") + "\"";
-				sql = format("--CREATE VIEW \"%s_list_view\" AS\nSELECT\n\t%s\nFROM \"%s\" AS a\nLIMIT %d", 
-					tbl, fields_csv, tbl, iMaxRows->value().tointeger());
-				sql += myjoins;
-			}
-			else if(sql == "insert") sql = format("INSERT INTO \"%s\"(%s)\nVALUES(%s)", tbl, fields_csv, fields_csv);
-			else if(sql == "update") {
-				fields_csv = "\"" + fields.concat("\"=?, \"") + "\"";
-				sql = format("UPDATE \"%s\" SET %s=?\nWHERE \"id\"=?", tbl, fields_csv);
-			}
-			else if(sql == "delete") sql = format("DELETE FROM \"%s\" WHERE \"id\"=?", tbl);
-			else if(sql == "create index") sql = format("CREATE INDEX \"%s_idx\" ON \"%s\"(\"field\" COLLATE NOCASE)", tbl, tbl);
-			else if(sql == "create trigger") sql = format("CREATE TRIGGER \"%s_trigger\"\nBEFORE/AFTER/INSTEAD OF INSERT, UPDATE, DELETE OF col_name ON \"%s\"\nFOR EACH ROW WHEN expr\nBEGIN\nEND;", tbl, tbl);
-			else if(sql == "drop table")
-			{
-				local table_type = db.exec_get_one("SELECT \"type\" FROM sqlite_master WHERE name='" + tbl + "'");
-				sql = format("DROP %s \"%s\"", table_type, tbl);
-			}
-			else if(sql == "dump table")
-			{
-				local result = blob(0, 8192);
-				local str_schema = getSchemaFor(tbl);
-				str_schema = str_schema.gsub("\n%s+", "\n\t");
-				result.write("BEGIN;\n\n", str_schema, ";");
-				getIndexesAndTriggersFor(tbl, result);
-				fields_csv = fields_csv.gsub(", ", ",\n\t");
-				result.write(format("\n\nINSERT INTO \"%s\" (\n\t%s\n\t) VALUES", tbl, fields_csv));
-
-				local result_size = result.len();
-				local stmt = db.prepare(format("SELECT * FROM \"%s\"", tbl));
-				local col_count = stmt.col_count();
-				
-				local SQLITE_INTEGER = stmt.SQLITE_INTEGER;
-				local SQLITE_FLOAT = stmt.SQLITE_FLOAT;
-				local SQLITE_NULL = stmt.SQLITE_NULL;
-				local SQLITE_TEXT = stmt.SQLITE_TEXT;
-				local SQLITE_BLOB = stmt.SQLITE_BLOB;
-				
-				while(stmt.next_row())
-				{
-					result.write("\n(");
-					for(local i=0; i < col_count; ++i)
-					{
-						local value = stmt.col(i);
-						if(i) result.write(",");
-						
-						local ctype = stmt.col_type(i);
-
-						if(ctype == SQLITE_INTEGER  || ctype == SQLITE_FLOAT)
-							result.write(value.tostring());
-							
-						else if(ctype == SQLITE_NULL) result.write("NULL");
-						else if(ctype == SQLITE_TEXT) result.write("'", value.gsub("'", "''") ,"'");
-						else if(ctype == SQLITE_BLOB) result.write(format("%q", value));
-						else result.write("??");
-					}
-					result.write("),");
-				}
-				stmt.finalize();
-				
-				if(result_size < result.len())
-				{
-					result.resize(result.len()-1); //delete last comma
-					result.write(";");
-				}
-				
-				result.write("\n\nCOMMIT;");
-				sql = result.tostring();
-			}
-			else if(sql == "references") sql = tbl;
-			else if(sql == "schema update") {
-				sql = genSchemaUpgrade(true);
-			}
-			else if(sql == "schema update norefs") {
-				sql = genSchemaUpgrade(false);
-			}
-			else if(sql == "sqlite_master update") {
-				local result = blob(0, 8192);
-				local schema_version = getSchemaVersion();
-				result.write("BEGIN;\n--PRAGMA schema_version; --> ", schema_version.tostring(), "\n\n");
-				result.write("PRAGMA writable_schema=ON;\n\n");
-				local str_schema = getSchemaFor(tbl);
-				str_schema = str_schema.gsub("\n%s+", "\n\t");
-				str_schema = str_schema.gsub("'", "''");
-				
-				result.write("UPDATE sqlite_master\nSET sql='", str_schema, "'\nWHERE type='table' AND name='", tbl, "';\n\n"); 
-				
-				local stmt = db.prepare(getIndexesSqlFor(tbl));
-				while(stmt.next_row())
-				{
-					local str = stmt.col(1);
-					if(::type(str) == "string") result.write("\n\n", str, ";");
-				}
-				stmt.finalize();
-
-				stmt = db.prepare(getTriggersSqlFor(tbl));
-				while(stmt.next_row())
-				{
-					local str = stmt.col(1);
-					if(::type(str) == "string") result.write("\n\n", str, ";");
-				}
-				stmt.finalize();
-				
-				result.write("\n\nDROP VIEW \"view_name\";\n\n");
-				result.write(getReferencesOnDBSchema(db, tbl));
-				result.write("\n\nCREATE VIEW  \"view_name\"  AS \"db_table_name\";");
-				result.write("\n\nPRAGMA schema_version=", (schema_version.tointeger() + 1).tostring(), 
-					";\n\nPRAGMA writable_schema=OFF;\n\nPRAGMA integrit_check;\n\nCOMMIT;");
-				sql = result.tostring();
-			}
-			else return;
-			edit_queries->value(sql);
+			local sql = SQLiteUtils.createQuery(db, tbl, option_query.text(), iMaxRows.value().tointeger(), getGridTableAttachedDB());
+			if(sql) edit_queries->value(sql);
 		}
 	}
 	
-	function refreshTabView(tbl)
+	function refreshTabView(tbl, attached_db)
 	{
 		local tab = tabView->value();
 		local sql = false;
 		local the_grid = false;
 		if(tab == viewFields)
 		{
-			sql = format("PRAGMA table_info(\"%s\");", tbl);
+			sql = SQLiteUtils.getTableInfoSqlFor(tbl, attached_db);
 			the_grid = grid_fields;
 		}
 		if(tab == viewIndexes)
 		{
-			sql = getIndexesSqlFor(tbl);
+			sql = SQLiteUtils.getIndexesSqlFor(tbl, attached_db);
 			the_grid = gridIndexes;
 		}
 		else if(tab == viewTriggers)
 		{
-			sql = getTriggersSqlFor(tbl);
+			sql = SQLiteUtils.getTriggersSqlFor(tbl, attached_db);
 			the_grid = gridTriggers;
 		}
 		else if(tab == groupSchema)
 		{
-			local str = getSchemaFor(tbl);
+			local str = SQLiteUtils.getSchemaFor(db, tbl, attached_db);
 			edit_schema->value(str);
 		}
 
@@ -973,7 +693,7 @@ class Sqlite3CC extends Sqlite3cc_Window {
 		local tbl = grid_tables.get_value(grid_tables.row(), 1);
 		if(tbl)
 		{
-			refreshTabView(tbl);
+			refreshTabView(tbl, getGridTableAttachedDB());
 		}
 	}
   
@@ -1001,18 +721,30 @@ class Sqlite3CC extends Sqlite3cc_Window {
 		return result;
 	}
 
-	function get_records_by_sql(grid, sql , named=false){
+	function get_records_by_sql(grid, sql , named=false, withBusyWorking=true){
+		if(withBusyWorking && !setBusyWorking(true)) return;
 		local cursor_wait = fl_cursor_wait();
 		local stmt = db.prepare(sql);
 		local rec_list = stmt.asArrayOfArrays(SQLite3Stmt.WITH_COL_NAMES | 
 			SQLite3Stmt.AS_STRING_ALWAYS | SQLite3Stmt.NULL_AS_EMPTY_STR);
 		grid.set_new_data(rec_list);
+		if(withBusyWorking) setBusyWorking(false);
 	}
 
-	function get_tables(){
-		local sql = "SELECT rowid AS 'rowid|ID|0', name AS 'name|Name|-1', type as 'type|Type|4' FROM sqlite_master WHERE (type='table' OR type='view') ORDER BY name";
-		get_records_by_sql(grid_tables, sql, true);
-		_the_schema_version = getSchemaVersion();
+	function get_tables(withBusyWorking=true){
+		local with_attached = table_len(attached_databases) > 0;
+		local sql_template = "SELECT rowid AS 'rowid|ID|0', name AS 'name|Name|-1', type as 'type|Type|4'";
+		if(with_attached) sql_template += ", '%s' AS 'db|DB|4'";
+		sql_template += " FROM %s.sqlite_master WHERE (type='table' OR type='view')";
+		local main_prefix = "main";
+		local sql = format(sql_template, main_prefix, main_prefix);
+		foreach(k,v in attached_databases)
+		{
+			sql += "\nUNION ALL\n" + format(sql_template, k, k);
+		}
+		sql += "\nORDER BY " + (with_attached ? "4,2" : "2");
+		get_records_by_sql(grid_tables, sql, true, withBusyWorking);
+		_the_schema_version = SQLiteUtils.getSchemaVersion(db, getGridTableAttachedDB());
 		_my_tables_data = grid_tables->_data;
 	}
 	
@@ -1025,11 +757,14 @@ class Sqlite3CC extends Sqlite3cc_Window {
 			set_label_dbf(the_db_file_name);
 			// SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_SUBLATIN_NA_LIKE
 			db = SQLite3(the_db_file_name);
+			db.progress_handler(1000, sqlite3_progress_handler, this.weakref());
 			//db.exec_dml("PRAGMA mmap_size=268435456;");
 			//db.trace(function(udata, sql){print(udata, ":", sql);}, "SQL");
 			//db.create_function("multiply3",3, multiply3); //SQLITE_DETERMINISTIC
 			get_tables();
 			Fl.preferences_set("last_db_file_name", the_db_file_name);
+			//_history_db_name = Fl.preferences_getUserdataPath() + "/sqlite3_cc_history.db";
+			//_history_db = SQLite3(_history_db_name);
 		}
 	}
 
@@ -1055,7 +790,7 @@ class Sqlite3CC extends Sqlite3cc_Window {
 			if (dbf){
 				//print(dbf)
 				local dbname = fl_input(_tr("Attach database with name ?"), "adb");
-				if (dbname && !attached_databases.get(dbname, false)){
+				if (dbname && !table_get(attached_databases, dbname, false)){
 					attached_databases[dbname] <- true;
 					local sql = format("ATTACH DATABASE '%s' as %s;", dbf, dbname);
 					db.exec_dml(sql);
@@ -1072,6 +807,39 @@ class Sqlite3CC extends Sqlite3cc_Window {
 				attached_databases = {};
 			}
 		}
+		else if(choice == menu_file_open_csv) {
+			local csvf = fl_file_chooser(_tr("Select a CSV file"), "*.csv", path);
+			local sep = fl_input("CSV Separator", ",");
+			local hasHeaders = fl_ask("The CSV first line is header ?");
+			if(sep)
+			{
+				local cursor_wait = fl_cursor_wait();
+				local max_rows = iMaxRows.value().tointeger();
+				local fd = file(csvf, "r");
+				local line;
+				local rec_list = [];
+				local isFirstLine = true;
+				while( (line = fd.read_line()) )
+				{
+					local record = line.split_csv(sep[0]);
+					if(isFirstLine)
+					{
+						if(!hasHeaders)
+						{
+							local col_count = record.len();
+							local header = array(col_count);
+							for(local i=0; i < col_count; ++i) header[i] = "col" + i;
+							rec_list.append(header);
+						}
+						isFirstLine = false;
+					}
+					rec_list.append(record);
+				}
+				fd.close();
+				grid_data.set_new_data(rec_list);
+				tabView->value(groupData);
+			}
+		}
 		else if(choice == menu_file_execute) {
 		}
 		else if(choice == menu_file_exit) os.exit();

+ 1 - 1
SquiLu-ourbiz/unify-code.nut

@@ -7,7 +7,7 @@
 local included = {};
 
 function unify_code(fname){
-	if(included.get(fname, false)) return "";
+	if(table_get(included, fname, false)) return "";
 	else included[fname] <- true;
 	
 	local fd = file(fname, "r");