mirror of https://github.com/gnosygnu/xowa
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
8.7 KiB
178 lines
8.7 KiB
/*
|
|
XOWA: the XOWA Offline Wiki Application
|
|
Copyright (C) 2012-2020 gnosygnu@gmail.com
|
|
|
|
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
|
|
or alternatively under the terms of the Apache License Version 2.0.
|
|
|
|
You may use XOWA according to either of these licenses as is most appropriate
|
|
for your project on a case-by-case basis.
|
|
|
|
The terms of each license can be found in the source code repository:
|
|
|
|
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
|
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
|
*/
|
|
package gplx.dbs.engines.sqlite; import gplx.*; import gplx.dbs.*; import gplx.dbs.engines.*;
|
|
import java.sql.*;
|
|
import gplx.core.stores.*; import gplx.dbs.engines.*; import gplx.dbs.engines.sqlite.*; import gplx.dbs.metas.*; import gplx.dbs.sqls.*;
|
|
import gplx.dbs.qrys.*;
|
|
import gplx.core.consoles.Console_adp_;
|
|
import gplx.core.consoles.Console_adp__sys;
|
|
import gplx.core.ios.IoItmFil;
|
|
|
|
import gplx.dbs.wkrs.SqlWkrMgr;
|
|
import gplx.dbs.wkrs.randoms.SqliteRandomWkr;
|
|
import org.sqlite.SQLiteConnection;
|
|
public class Sqlite_engine extends Db_engine_sql_base {
|
|
private final Sqlite_txn_mgr txn_mgr; private final Sqlite_schema_mgr schema_mgr;
|
|
Sqlite_engine() {
|
|
this.txn_mgr = new Sqlite_txn_mgr(this);
|
|
this.schema_mgr = new Sqlite_schema_mgr(this);
|
|
}
|
|
@Override public String Tid() {return Sqlite_conn_info.Key_const;}
|
|
@Override public gplx.dbs.sqls.Sql_qry_wtr Sql_wtr() {return Sql_qry_wtr_.New__sqlite();}
|
|
public void CtorConn(SqlWkrMgr wkrMgr) {
|
|
wkrMgr.Set(new SqliteRandomWkr());
|
|
}
|
|
|
|
@Override public Db_engine New_clone(Db_conn_info connectInfo) {
|
|
Sqlite_engine rv = new Sqlite_engine();
|
|
rv.Ctor(connectInfo);
|
|
return rv;
|
|
}
|
|
@Override public DataRdr New_rdr(ResultSet rdr, String commandText) {return Sqlite_rdr.new_(rdr, commandText);}
|
|
@Override public Db_rdr New_rdr_clone() {return new Db_rdr__sqlite();}
|
|
@Override public void Env_db_attach(String alias, Db_conn conn) {
|
|
Db_conn_info cs_obj = conn.Conn_info(); if (!String_.Eq(cs_obj.Key(), Sqlite_conn_info.Key_const)) throw Err_.new_("dbs", "must attach to sqlite databases", "conn", cs_obj.Raw());
|
|
Sqlite_conn_info cs = (Sqlite_conn_info)cs_obj;
|
|
Env_db_attach(alias, cs.Url());
|
|
}
|
|
@Override public void Env_db_attach(String alias, Io_url db_url) {Exec_as_int(String_.Format("ATTACH '{0}' AS {1};", db_url.Raw(), alias));}
|
|
@Override public void Env_db_detach(String alias) {Exec_as_int(String_.Format("DETACH {0};", alias));}
|
|
@Override public void Txn_bgn(String name) {txn_mgr.Txn_bgn(name);}
|
|
@Override public String Txn_end() {return txn_mgr.Txn_end();}
|
|
@Override public void Txn_cxl() {txn_mgr.Txn_cxl();}
|
|
@Override public void Txn_sav() {txn_mgr.Txn_sav();}
|
|
@Override public Dbmeta_tbl_mgr Meta_mgr() {return schema_mgr.Tbl_mgr();}
|
|
@Override public boolean Meta_tbl_exists(String tbl) {return schema_mgr.Tbl_exists(tbl);}
|
|
@Override public boolean Meta_fld_exists(String tbl, String fld) {return schema_mgr.Fld_exists(tbl, fld);}
|
|
@Override public boolean Meta_idx_exists(String idx) {return schema_mgr.Idx_exists(idx);}
|
|
@Override public Db_stmt Stmt_by_qry(Db_qry qry) {return new Sqlite_stmt(this, qry);}
|
|
private static boolean loaded = false;
|
|
protected void Meta_tbl_gather_hook() {throw Err_.new_unimplemented();}
|
|
@gplx.Internal @Override protected Connection Conn_make() {
|
|
if (!loaded) {
|
|
try {
|
|
Class.forName("org.sqlite.JDBC");
|
|
}
|
|
catch (ClassNotFoundException e) {throw Err_.new_exc(e, "db", "could not load sqlite jdbc driver");}
|
|
loaded = true;
|
|
}
|
|
|
|
// init vars for opening connection
|
|
Sqlite_conn_info conn_info_as_sqlite = (Sqlite_conn_info)conn_info;
|
|
Io_url sqlite_fs_url = conn_info_as_sqlite.Url();
|
|
String sqlite_db_url = "jdbc:sqlite://" + String_.Replace(sqlite_fs_url.Raw(), "\\", "/");
|
|
|
|
// set open_mode flag if conn is read-only; needed else all SELECT queries will be very slow; DATE:2016-09-03
|
|
IoItmFil sqlite_fs_itm = Io_mgr.Instance.QueryFil(sqlite_fs_url);
|
|
boolean read_only = sqlite_fs_itm.Exists() // NOTE: must check if it exists; else missing-file will be marked as readonly connection, and missing-file will sometimes be dynamically created as read-write; DATE:2016-09-04
|
|
&& Io_mgr.Instance.Query_read_only(sqlite_fs_url, Sqlite_engine_.Read_only_detection);
|
|
Keyval[] props = read_only
|
|
? Keyval_.Ary(Keyval_.new_("open_mode", "1"))
|
|
: Keyval_.Ary_empty;
|
|
if (read_only) {
|
|
Gfo_usr_dlg_.Instance.Note_many("", "", "Sqlite db opened as read-only: url=~{0}", sqlite_fs_url.Xto_api());
|
|
}
|
|
|
|
// open connection
|
|
Connection rv = Conn__new_by_url_and_props(sqlite_db_url, props);
|
|
|
|
// set busyTimeout; needed else multiple processes accessing same db can cause "database is locked" error; DATE:2016-09-03
|
|
SQLiteConnection rv_as_sqlite = (org.sqlite.SQLiteConnection)rv;
|
|
try {rv_as_sqlite.setBusyTimeout(10000);}
|
|
catch (SQLException e) {Gfo_usr_dlg_.Instance.Warn_many("", "", "failed to set busy timeout; err=~{0}", Err_.Message_gplx_log(e));}
|
|
return rv;
|
|
}
|
|
public static final Sqlite_engine Instance = new Sqlite_engine();
|
|
}
|
|
class Db_rdr__sqlite extends Db_rdr__basic { @Override public byte Read_byte(String k) {try {return (byte)Int_.Cast(rdr.getObject(k));} catch (Exception e) {throw Err_.new_exc(e, "db", "read failed", "k", k, "type", Byte_.Cls_val_name);}}
|
|
@Override public boolean Read_bool_by_byte(String k) {
|
|
try {
|
|
int val = rdr.getInt(k);
|
|
return val == 1;
|
|
} catch (Exception e) {throw Err_.new_exc(e, "db", "read failed", "i", k, "type", Bool_.Cls_val_name);}
|
|
}
|
|
@Override public long Read_long(String k) {
|
|
try {
|
|
long val = rdr.getLong(k);
|
|
Number n = (Number)val;
|
|
return n.longValue();
|
|
} catch (Exception e) {throw Err_.new_exc(e, "db", "read failed", "i", k, "type", Long_.Cls_val_name);}
|
|
}
|
|
@Override public float Read_float(String k) {
|
|
try {
|
|
Double val = (Double)rdr.getDouble(k);
|
|
return val == null ? Float.NaN : val.floatValue();
|
|
} catch (Exception e) {throw Err_.new_exc(e, "db", "read failed:", "i", k, "type", Float_.Cls_val_name);}
|
|
}
|
|
@Override public DateAdp Read_date_by_str(String k) {
|
|
try {
|
|
String val = rdr.getString(k);
|
|
return val == null ? null : DateAdp_.parse_fmt(val, "yyyyMMdd_HHmmss");
|
|
} catch (Exception e) {throw Err_.new_exc(e, "db", "read failed", "i", k, "type", DateAdp_.Cls_ref_type);}
|
|
}
|
|
// @Override public DecimalAdp ReadDecimalOr(String key, DecimalAdp or) {
|
|
// Object val = Read(key);
|
|
// Double d = ((Double)val);
|
|
// return val == null ? null : DecimalAdp_.double_(d);
|
|
// }
|
|
}
|
|
class Sqlite_rdr extends Db_data_rdr { @Override public float ReadFloat(String key) {return ReadFloatOr(key, Float.NaN);}
|
|
@Override public float ReadFloatOr(String key, float or) {
|
|
Object val = Read(key);
|
|
Double d = ((Double)val);
|
|
return val == null ? or : d.floatValue();
|
|
}
|
|
@Override public Decimal_adp ReadDecimal(String key) {return ReadDecimalOr(key, null);}
|
|
@Override public Decimal_adp ReadDecimalOr(String key, Decimal_adp or) {
|
|
Object val = Read(key);
|
|
Double d = ((Double)val);
|
|
return val == null ? or : Decimal_adp_.double_(d);
|
|
}
|
|
@Override public DateAdp ReadDate(String key) {return ReadDateOr(key, null);}
|
|
@Override public DateAdp ReadDateOr(String key, DateAdp or) {
|
|
Object val = Read(key);
|
|
return val == null ? or : DateAdp_.parse_fmt((String)val, "M/dd/yyyy hh:mm tt");
|
|
}
|
|
@Override public boolean ReadBool(String key) {return ReadBoolOr(key, false);}
|
|
@Override public boolean ReadBoolOr(String key, boolean or) {
|
|
Object val = Read(key);
|
|
return val == null ? or : Int_.Cast(val) == 1;
|
|
}
|
|
@Override public byte ReadByte(String key) {return ReadByteOr(key, Byte_.Zero);}
|
|
@Override public byte ReadByteOr(String key, byte or) {
|
|
Object val = Read(key);
|
|
return val == null ? or : (byte)Int_.Cast(val);
|
|
}
|
|
@Override public long ReadLong(String key) {return ReadLongOr(key, Long_.Min_value);}
|
|
@Override public long ReadLongOr(String key, long or) {
|
|
Object val = Read(key);
|
|
if (val == null) return or;
|
|
Number n = (Number)val;
|
|
return n.longValue();
|
|
}
|
|
public static Sqlite_rdr new_(ResultSet rdr, String commandText) {
|
|
Sqlite_rdr rv = new Sqlite_rdr();
|
|
rv.ctor_db_data_rdr(rdr, commandText);
|
|
return rv;
|
|
} Sqlite_rdr() {}
|
|
}
|
|
class Sqlite_stmt extends gplx.dbs.qrys.Db_stmt_cmd { public Sqlite_stmt(Db_engine engine, Db_qry qry) {super(engine, qry);}
|
|
@Override protected Db_stmt Add_date(boolean where, String k, DateAdp v) {
|
|
if (k == Dbmeta_fld_itm.Key_null) return this; // key is explicitly null; ignore; allows version_2+ type definitions
|
|
return super.Add_str(where, k, v.XtoStr_fmt_iso_8561());
|
|
}
|
|
}
|