| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071 |
- 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;
- stmt_insert_params = null;
- stmt_clear_params = 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
- );
- CREATE VIEW IF NOT EXISTS __app_sql_list_view AS
- SELECT
- a.id,
- a._mdate_,
- a.name
- FROM __app_sql AS a;
- CREATE TABLE IF NOT EXISTS __app_sql_parameters (
- id INTEGER PRIMARY KEY NOT NULL,
- _version_ INTEGER NOT NULL DEFAULT 0,
- _cdate_ DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
- _mdate_ DATETIME,
- app_sql_id integer not null references __app_sql(id),
- name VARCHAR NOT NULL COLLATE NOCASE,
- default_value VARCHAR COLLATE NOCASE,
- field_type VARCHAR COLLATE NOCASE,
- notes TEXT COLLATE NOCASE,
- CONSTRAINT app_sql_id_name_unique UNIQUE(app_sql_id, name)
- );
- ]==];
- 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=?");
- stmt_clear_params = db.prepare("DELETE FROM __app_sql_parameters WHERE app_sql_id=?");
- stmt_insert_params = db.prepare("INSERT INTO __app_sql_parameters(app_sql_id, name, default_value, field_type, notes) VALUES(?,?,?,?,?)");
- }
- }
-
- function doFinalize(db)
- {
- if(stmt_get)
- {
- stmt_get.finalize();
- stmt_get = null;
- stmt_set.finalize();
- stmt_get_by_name.finalize();
- stmt_clear_params.finalize();
- stmt_insert_params.finalize();
- }
- }
-
- function saveSqlParameters(sql_id, sql)
- {
- stmt_clear_params.bind(1, sql_id);
- stmt_clear_params.step();
- stmt_clear_params.reset();
- local self = this;
- //{$field_name:default_value:field_type:field_notes}
- sql.gmatch("(%b{})", function(m){
- if(m[1] == '$')
- {
- local field_name, default_value, field_type, field_notes;
- local ary = m.slice(2,-1).split(':');
- if(ary.len() > 0)
- {
- foreach(idx, val in ary)
- {
- val = val.trim();
- switch(idx)
- {
- case 0: field_name = val; break;
- case 1: default_value = val; break;
- case 2: field_type = val; break;
- case 4: field_notes = val; break;
- }
- }
- local stmt_insert_params = self.stmt_insert_params;
- stmt_insert_params.bind(1, sql_id);
- stmt_insert_params.bind(2, field_name);
- stmt_insert_params.bind_empty_null(3, default_value);
- stmt_insert_params.bind_empty_null(4, field_type);
- stmt_insert_params.bind_empty_null(5, field_notes);
- stmt_insert_params.step();
- stmt_insert_params.reset();
- }
- }
- return true;
- });
- }
-
- //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], null);
- if(val == null && (ary.len() > 1))
- {
- //we'll add the default parameter to params
- val = 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();
- saveSqlParameters(id, sql_seed);
- }
- 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;
- }
- }
|