2017-03-02 14:37:32 +00:00
using Sqlite ;
namespace Qlite {
public class Table {
protected Database db ;
public string name { get ; private set ; }
2017-04-16 13:11:00 +00:00
protected Column [ ] ? columns ;
private string constraints = " " ;
2017-04-23 08:23:11 +00:00
private string [ ] post_statements = { } ;
2018-06-17 23:47:43 +00:00
private string [ ] create_statements = { } ;
internal Column [ ] ? fts_columns ;
2017-03-02 14:37:32 +00:00
public Table ( Database db , string name ) {
this . db = db ;
this . name = name ;
}
2017-04-16 13:11:00 +00:00
public void init ( Column [ ] columns , string constraints = " " ) {
2017-03-02 14:37:32 +00:00
this . columns = columns ;
2017-03-09 20:46:16 +00:00
this . constraints = constraints ;
2018-06-27 14:58:10 +00:00
foreach ( Column c in columns ) {
c . table = this ;
}
2017-03-09 20:46:16 +00:00
}
2018-06-17 23:47:43 +00:00
public void fts ( Column [ ] columns ) {
if ( fts_columns ! = null ) error ( " Only one FTS index may be used per table. " ) ;
fts_columns = columns ;
string cs = " " ;
string cnames = " " ;
string cnews = " " ;
foreach ( Column c in columns ) {
2018-06-27 14:58:10 +00:00
cs + = @" , $(c.to_column_definition()) " ;
2018-06-17 23:47:43 +00:00
cnames + = @" , $(c.name) " ;
cnews + = @" , new.$(c.name) " ;
}
add_create_statement ( @" CREATE VIRTUAL TABLE IF NOT EXISTS _fts_$name USING fts4(tokenize=unicode61, content= \" $name \" $cs) " ) ;
add_post_statement ( @" CREATE TRIGGER IF NOT EXISTS _fts_bu_$(name) BEFORE UPDATE ON $name BEGIN DELETE FROM _fts_$name WHERE docid=old.rowid; END " ) ;
add_post_statement ( @" CREATE TRIGGER IF NOT EXISTS _fts_bd_$(name) BEFORE DELETE ON $name BEGIN DELETE FROM _fts_$name WHERE docid=old.rowid; END " ) ;
add_post_statement ( @" CREATE TRIGGER IF NOT EXISTS _fts_au_$(name) AFTER UPDATE ON $name BEGIN INSERT INTO _fts_$name(docid$cnames) VALUES(new.rowid$cnews); END " ) ;
add_post_statement ( @" CREATE TRIGGER IF NOT EXISTS _fts_ai_$(name) AFTER INSERT ON $name BEGIN INSERT INTO _fts_$name(docid$cnames) VALUES(new.rowid$cnews); END " ) ;
}
public void fts_rebuild ( ) {
if ( fts_columns = = null ) error ( " FTS not available on this table. " ) ;
try {
db . exec ( @" INSERT INTO _fts_$name(_fts_$name) VALUES('rebuild'); " ) ;
} catch ( Error e ) {
2019-12-15 18:03:02 +00:00
critical ( @" Qlite Error: Rebuilding FTS index: $(e.message) " ) ;
2018-06-17 23:47:43 +00:00
}
}
2017-03-09 20:46:16 +00:00
public void unique ( Column [ ] columns , string ? on_conflict = null ) {
2017-04-16 13:11:00 +00:00
constraints + = " , UNIQUE ( " ;
2017-03-09 20:46:16 +00:00
bool first = true ;
2017-03-12 18:33:31 +00:00
foreach ( Column c in columns ) {
2017-03-09 20:46:16 +00:00
if ( ! first ) constraints + = " , " ;
constraints + = c . name ;
first = false ;
}
constraints + = " ) " ;
if ( on_conflict ! = null ) {
2017-05-21 21:30:30 +00:00
constraints + = " ON CONFLICT " + ( ! ) on_conflict ;
2017-03-09 20:46:16 +00:00
}
2017-03-02 14:37:32 +00:00
}
2017-04-23 08:23:11 +00:00
public void add_post_statement ( string stmt ) {
post_statements + = stmt ;
}
2018-06-17 23:47:43 +00:00
public void add_create_statement ( string stmt ) {
create_statements + = stmt ;
}
2017-04-23 08:23:11 +00:00
public void index ( string index_name , Column [ ] columns , bool unique = false ) {
string stmt = @" CREATE $(unique ? " UNIQUE " : " " ) INDEX IF NOT EXISTS $index_name ON $name ( " ;
bool first = true ;
foreach ( Column c in columns ) {
if ( ! first ) stmt + = " , " ;
stmt + = c . name ;
first = false ;
}
stmt + = " ) " ;
add_post_statement ( stmt ) ;
}
2017-10-28 21:48:07 +00:00
private void ensure_init ( ) {
if ( columns = = null ) error ( " Table %s was not initialized, call init() " , name ) ;
2017-03-02 14:37:32 +00:00
}
2017-10-28 21:48:07 +00:00
public QueryBuilder select ( Column [ ] ? columns = null ) {
2017-03-02 14:37:32 +00:00
ensure_init ( ) ;
return db . select ( columns ) . from ( this ) ;
}
2018-06-17 23:47:43 +00:00
private MatchQueryBuilder match_query ( ) {
ensure_init ( ) ;
return db . match_query ( this ) ;
}
public MatchQueryBuilder match ( Column < string > column , string query ) {
return match_query ( ) . match ( column , query ) ;
}
2017-10-28 21:48:07 +00:00
public InsertBuilder insert ( ) {
2017-03-02 14:37:32 +00:00
ensure_init ( ) ;
return db . insert ( ) . into ( this ) ;
}
2017-10-28 21:48:07 +00:00
public UpdateBuilder update ( ) {
2017-03-02 14:37:32 +00:00
ensure_init ( ) ;
return db . update ( this ) ;
}
2017-10-28 21:48:07 +00:00
public UpsertBuilder upsert ( ) {
2017-08-25 22:05:36 +00:00
ensure_init ( ) ;
return db . upsert ( this ) ;
}
2017-10-28 21:48:07 +00:00
public DeleteBuilder delete ( ) {
2017-03-02 14:37:32 +00:00
ensure_init ( ) ;
return db . delete ( ) . from ( this ) ;
}
2017-10-28 21:48:07 +00:00
public RowOption row_with < T > ( Column < T > column , T value ) {
2017-03-02 14:37:32 +00:00
ensure_init ( ) ;
2017-10-28 21:48:07 +00:00
if ( ! column . unique & & ! column . primary_key ) error ( " %s is not suited to identify a row, but used with row_with() " , column . name ) ;
2017-03-02 14:37:32 +00:00
return select ( ) . with ( column , " = " , value ) . row ( ) ;
}
2017-10-28 21:48:07 +00:00
public bool is_known_column ( string column ) {
2017-03-02 14:37:32 +00:00
ensure_init ( ) ;
foreach ( Column c in columns ) {
if ( c . name = = column ) return true ;
}
return false ;
}
2017-10-28 21:48:07 +00:00
public void create_table_at_version ( long version ) {
2017-03-02 14:37:32 +00:00
ensure_init ( ) ;
string sql = @" CREATE TABLE IF NOT EXISTS $name ( " ;
2018-06-19 10:26:31 +00:00
bool first = true ;
2017-03-12 18:33:31 +00:00
for ( int i = 0 ; i < columns . length ; i + + ) {
2017-03-02 14:37:32 +00:00
Column c = columns [ i ] ;
if ( c . min_version < = version & & c . max_version > = version ) {
2018-11-10 14:05:14 +00:00
sql + = @" $(!first ? " , " : " " ) $(c.to_column_definition()) " ;
2018-06-19 10:26:31 +00:00
first = false ;
2017-03-02 14:37:32 +00:00
}
}
2017-04-16 13:11:00 +00:00
sql + = @" $constraints) " ;
2017-10-29 14:15:28 +00:00
try {
db . exec ( sql ) ;
} catch ( Error e ) {
2018-11-10 14:05:14 +00:00
error ( @" Qlite Error: Create table at version: $(e.message) " ) ;
2017-10-29 14:15:28 +00:00
}
2018-06-17 23:47:43 +00:00
foreach ( string stmt in create_statements ) {
try {
db . exec ( stmt ) ;
} catch ( Error e ) {
2018-11-10 14:05:14 +00:00
error ( @" Qlite Error: Create table at version: $(e.message) " ) ;
2018-06-17 23:47:43 +00:00
}
}
2017-03-02 14:37:32 +00:00
}
2017-10-28 21:48:07 +00:00
public void add_columns_for_version ( long old_version , long new_version ) {
2017-03-02 14:37:32 +00:00
ensure_init ( ) ;
foreach ( Column c in columns ) {
2017-04-03 17:20:31 +00:00
if ( c . min_version < = new_version & & c . max_version > = new_version & & c . min_version > old_version ) {
2017-10-29 14:15:28 +00:00
try {
2018-06-27 14:58:10 +00:00
db . exec ( @" ALTER TABLE $name ADD COLUMN $(c.to_column_definition()) " ) ;
2017-10-29 14:15:28 +00:00
} catch ( Error e ) {
2019-12-15 18:03:02 +00:00
critical ( @" Qlite Error: Add columns for version: $(e.message) " ) ;
2017-10-29 14:15:28 +00:00
}
2017-03-02 14:37:32 +00:00
}
}
}
2017-10-28 21:48:07 +00:00
public void delete_columns_for_version ( long old_version , long new_version ) {
2017-03-12 18:33:31 +00:00
bool column_deletion_required = false ;
2017-04-16 13:11:00 +00:00
string column_list = " " ;
2017-03-12 18:33:31 +00:00
foreach ( Column c in columns ) {
if ( c . min_version < = new_version & & c . max_version > = new_version ) {
2017-04-16 13:11:00 +00:00
if ( column_list = = " " ) {
2017-03-12 18:33:31 +00:00
column_list = c . name ;
} else {
column_list + = " , " + c . name ;
}
}
if ( ! ( c . min_version < = new_version & & c . max_version > = new_version ) & & c . min_version < = old_version & & c . max_version > = old_version ) {
column_deletion_required = true ;
}
}
if ( column_deletion_required ) {
2017-10-29 14:15:28 +00:00
try {
db . exec ( @" ALTER TABLE $name RENAME TO _$(name)_$old_version " ) ;
create_table_at_version ( new_version ) ;
db . exec ( @" INSERT INTO $name ($column_list) SELECT $column_list FROM _$(name)_$old_version " ) ;
db . exec ( @" DROP TABLE _$(name)_$old_version " ) ;
} catch ( Error e ) {
2018-11-10 14:05:14 +00:00
error ( @" Qlite Error: Delete columns for version change: $(e.message) " ) ;
2017-10-29 14:15:28 +00:00
}
2017-03-12 18:33:31 +00:00
}
2017-03-02 14:37:32 +00:00
}
2017-04-23 08:23:11 +00:00
2017-10-28 21:48:07 +00:00
internal void post ( ) {
2017-04-23 08:23:11 +00:00
foreach ( string stmt in post_statements ) {
2017-10-29 14:15:28 +00:00
try {
db . exec ( stmt ) ;
} catch ( Error e ) {
2018-11-10 14:05:14 +00:00
error ( @" Qlite Error: Post: $(e.message) " ) ;
2017-10-29 14:15:28 +00:00
}
2017-04-23 08:23:11 +00:00
}
}
2017-03-02 14:37:32 +00:00
}
2017-10-28 21:48:07 +00:00
}