├── Makefile ├── README └── sqlite-commander.cs /Makefile: -------------------------------------------------------------------------------- 1 | SOURCES = sqlite-commander.cs 2 | 3 | Autobahn.exe: $(SOURCES) Makefile 4 | gmcs -debug $(SOURCES) -pkg:mono-curses -r:System.Data.dll -r:Mono.Data.SqliteClient.dll 5 | 6 | run: Autobahn.exe 7 | mono --debug sqlite-commander.exe .sqlite; stty sane 8 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Sqlite-Commander 2 | ================ 3 | 4 | Sqlite-Commander is a project that is aimed to display sqlite databases in Terminal using ncurses. Mono.NCurses is used as the CUI framework. The name is inspired from Midnight-Commander written by Miguel De Icaza. 5 | 6 | All the source code is available to you in LGPL v2.1 only. 7 | 8 | Drop me a mail if you like the software. 9 | 10 | 11 | Features 12 | ======== 13 | + Shows the list of tables in the left pane. 14 | + Allows you to navigate the list of tables using arrow keys. 15 | + Shows the first 30 records for the selected table in the right pane 16 | + Non-printable columns are shown with a ??? 17 | + Maximum of 30 characters are displayed per column. Column data exceeding this length are shown with "..." 18 | 19 | 20 | ToDo 21 | ==== 22 | There are few things which will be good to have. But I don't see a pressing need for these features in my workflow. So I have not implemented these. But patches are more than welcome. If you feel very interested about some of these items, send me a mail and I may do it :) Sending mails with patches for these features will be even more awesome. 23 | 24 | + Add a scroll bar and show more than 30 columns. Should be moderate/easy to implement. 25 | + Add a dialog box to execute any random SQL statement. Should be fairly trivial to implement. 26 | + Create Events such that pressing Enter on the records list will pass the currently selected record to a custom script/command. 27 | + Copy selections of records to clipboard. Should be moderate/easy to implement. 28 | 29 | 30 | Hacking Sqlite-Commander 31 | ========================= 32 | 1) Install mono-devel packages, make 33 | 2) Checkout MonoCurses package from Mono source code repositories: git clone https://github.com/mono/mono-curses.git 34 | 3) ./configure --prefix=/usr ; sudo make install 35 | 4) Checkout Sqlite-Commander sources from github: git clone https://github.com/curiosity/sqlite-commander.git 36 | 5) Substitute "" in the Makefile with your sqlite file path. 37 | 6) make run 38 | 39 | You can also execute sqlite-commander manually and pass the database file path as a command line option. 40 | -------------------------------------------------------------------------------- /sqlite-commander.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Sqlite-Commander: NCurses Interface for your SQLite databases 3 | * 4 | * Author: Sankar P 5 | * 6 | * License: LGPL V2.1 Only 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; 11 | * version 2.1 of the License only. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | * 22 | */ 23 | 24 | using System; 25 | using System.Collections.Generic; 26 | using System.Collections; 27 | using System.Data; 28 | using Mono.Terminal; 29 | using Mono.Data.SqliteClient; 30 | 31 | namespace SqliteCommander 32 | { 33 | public class TablesList : IListProvider 34 | { 35 | public List items = new List (); 36 | public ListView view; 37 | 38 | public event TableSelectionChanged newSel; 39 | public EventArgs e = null; 40 | 41 | public delegate void TableSelectionChanged(TablesList t, EventArgs e); 42 | 43 | public void Render (int line, int col, int width, int item) 44 | { 45 | Curses.addstr (items[item]); 46 | } 47 | 48 | public bool AllowMark { 49 | get { 50 | return false; 51 | } 52 | } 53 | 54 | public int Items { 55 | get { 56 | return items.Count; 57 | } 58 | } 59 | 60 | public bool IsMarked (int item) 61 | { 62 | return false; 63 | } 64 | 65 | public bool ProcessKey (int ch) 66 | { 67 | return false; 68 | } 69 | 70 | public void SelectedChanged () 71 | { 72 | newSel (this, e); 73 | return; 74 | } 75 | 76 | public void SetListView (ListView target) 77 | { 78 | view = target; 79 | } 80 | 81 | public void Add (string s) 82 | { 83 | items.Add (s); 84 | } 85 | } 86 | 87 | public class RecordsList : IListProvider 88 | { 89 | public List items = new List (); 90 | public ListView view; 91 | 92 | static int MAX_CHARS_PER_COLUMN = 30; 93 | 94 | public void Render (int line, int col, int width, int item) 95 | { 96 | string record = ""; 97 | List l = (List) items[item]; 98 | 99 | foreach (string column in l) { 100 | if (column.Length > MAX_CHARS_PER_COLUMN) 101 | record = record + column.Substring (0, MAX_CHARS_PER_COLUMN) + "... "; 102 | else 103 | record = record + column + " "; 104 | } 105 | Curses.addstr (record); 106 | } 107 | 108 | public bool AllowMark { 109 | get { 110 | return false; 111 | } 112 | } 113 | 114 | public int Items { 115 | get { 116 | return items.Count; 117 | } 118 | } 119 | 120 | public bool IsMarked (int item) 121 | { 122 | return false; 123 | } 124 | 125 | public bool ProcessKey (int ch) 126 | { 127 | return false; 128 | } 129 | 130 | public void SelectedChanged () 131 | { 132 | return; 133 | } 134 | 135 | public void SetListView (ListView target) 136 | { 137 | view = target; 138 | } 139 | 140 | public void Add (IEnumerable s) 141 | { 142 | items.Add (s); 143 | } 144 | } 145 | 146 | public class Shell : Container 147 | { 148 | IDbCommand command; 149 | IDbConnection dbConnection; 150 | IDataReader reader; 151 | 152 | TablesList tables; 153 | RecordsList records; 154 | ListView tables_view; 155 | ListView records_view; 156 | 157 | Frame recordsFrame; 158 | 159 | static int TABLES_WIDTH = 25; 160 | static int MAX_NUMBER_OF_RECORDS = 30; 161 | 162 | string currentTable; 163 | string sql; 164 | 165 | public void UpdateRecordsView () 166 | { 167 | 168 | /* In C#, IIUC, it is better to create a new List object and 169 | * let the GC clear the old list object, is faster than 170 | * emptying the list manually and adding items again. 171 | */ 172 | records = new RecordsList (); 173 | sql = "SELECT * FROM " + currentTable; 174 | command.CommandText = sql; 175 | reader = command.ExecuteReader (); 176 | int n = 0, col_count; 177 | while (reader.Read () && n++ < MAX_NUMBER_OF_RECORDS) { 178 | List record; 179 | record = new List (); 180 | 181 | for (col_count = 0; col_count < reader.FieldCount; ++col_count) { 182 | try { 183 | //Console.WriteLine (col_count + "Adding column " + reader.GetString (col_count)); 184 | record.Add (reader.GetString (col_count)); 185 | } catch (System.NullReferenceException ex){ 186 | record.Add ("???"); 187 | } 188 | } 189 | records.Add (record); 190 | } 191 | 192 | if (records_view != null) 193 | recordsFrame.Remove (records_view); 194 | 195 | records_view = new ListView (1, 1, 1, records.Items, records); 196 | recordsFrame.Add (records_view); 197 | recordsFrame.Redraw (); 198 | } 199 | 200 | public Shell (string filename) : base (0, 0, Application.Cols, Application.Lines) 201 | { 202 | string connectionString; 203 | 204 | tables = new TablesList (); 205 | 206 | connectionString = "URI=file:" + filename; 207 | dbConnection = (IDbConnection) new SqliteConnection(connectionString); 208 | dbConnection.Open (); 209 | command = dbConnection.CreateCommand (); 210 | sql = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"; 211 | command.CommandText = sql; 212 | reader = command.ExecuteReader (); 213 | while (reader.Read ()) { 214 | tables.Add (reader.GetString (0)); 215 | } 216 | currentTable = tables.items [0]; 217 | 218 | Frame tablesFrame = new Frame ("Tables"); 219 | Add (tablesFrame); 220 | 221 | tablesFrame.x = 0; 222 | tablesFrame.y = 0; 223 | 224 | tablesFrame.w = TABLES_WIDTH; 225 | tablesFrame.h = Application.Lines; 226 | 227 | tables_view = new ListView (1, 1, 1, tables.Items, tables); 228 | tablesFrame.Add (tables_view); 229 | 230 | recordsFrame = new Frame ("Records"); 231 | recordsFrame.x = TABLES_WIDTH; 232 | recordsFrame.y = 0; 233 | recordsFrame.w = Application.Cols - TABLES_WIDTH - 1; 234 | recordsFrame.h = Application.Lines; 235 | Add (recordsFrame); 236 | 237 | UpdateRecordsView (); 238 | 239 | tables.newSel += new TablesList.TableSelectionChanged (UpdateRecordsForNewSelectedTable); 240 | } 241 | 242 | private void UpdateRecordsForNewSelectedTable (TablesList t, EventArgs e) 243 | { 244 | currentTable = t.items[tables_view.Selected]; 245 | UpdateRecordsView (); 246 | } 247 | 248 | ~Shell () 249 | { 250 | // clean up 251 | reader.Close (); reader = null; 252 | command.Dispose (); command = null; 253 | dbConnection.Close (); dbConnection = null; 254 | } 255 | 256 | static void Main (String [] args) 257 | { 258 | if (args.Length != 1) 259 | Console.WriteLine ("Insufficient number of parameters"); 260 | else { 261 | Application.Init (false); 262 | Shell s = new Shell (args [0]); 263 | Application.Run (s); 264 | } 265 | } 266 | } 267 | } 268 | --------------------------------------------------------------------------------