38

For kicks I'm writing a "schema documentation" tool that generates a description of the tables and relationships in a database. I'm currently shimming it to work with SQLite.

I've managed to extract the names of all the tables in a SQLite database via a query on the sqlite_master table. For each table name, I then fire off a simple

select * from <table name> 

query, then use the sqlite3_column_count() and sqlite3_column_name() APIs to collect the column names, which I further feed to sqlite3_table_column_metadata() to get additional info. Simple enough, right?

The problem is that it only works for tables that are not empty. That is, the sqlite_column_*() APIs are only valid if sqlite_step() has returned SQLITE_ROW, which is not the case for empty tables.

So the question is, how can I discover column names for empty tables? Or, more generally, is there a better way to get this type of schema info in SQLite?

I feel like there must be another hidden sqlite_xxx table lurking somewhere containing this info, but so far have not been able to find it.

1
  • 2
    Thanks to all. I mistakenly thought the PRAGMA interface was only for the sqlite3 command line client. Commented May 30, 2009 at 4:07

6 Answers 6

64
sqlite> .header on sqlite> .mode column sqlite> create table ABC(A TEXT, B VARCHAR); sqlite> pragma table_info(ABC); cid name type notnull dflt_value pk ---------- ---------- ---------- ---------- ---------- ---------- 0 A TEXT 0 0 1 B VARCHAR 0 0 
Sign up to request clarification or add additional context in comments.

3 Comments

Looks like that works on the command line--how can I achieve the same effect programatically?
Thanks--this worked like a charm & is much simpler than what I was doing. Cheers!
@Drew Hall This works also as a query executed programatically, not only from command line.
10

Execute the query:

PRAGMA table_info( your_table_name ); 

Documentation

Comments

5

PRAGMA table_info( your_table_name ); doesn't work in HTML5 SQLite.

Here is a small HTML5 SQLite JavaScript Snippet which gets the column names from your_table_name even if its empty. Hope its helpful.

tx.executeSql('SELECT name, sql FROM sqlite_master WHERE type="table" AND name = "your_table_name";', [], function (tx, results) { var columnParts = results.rows.item(0).sql.replace(/^[^\(]+\(([^\)]+)\)/g, '$1').split(','); var columnNames = []; for(i in columnParts) { if(typeof columnParts[i] === 'string') columnNames.push(columnParts[i].split(" ")[0]); } console.log(columnNames); ///// Your code which uses the columnNames; }); 

2 Comments

It looks like if your table has more than one column, columns 2 - n don't get processed because of leading whitespace. This can be fixed by calling the trim() function on the string (or implementing it yourself for browsers that don't yet support trim()). Calling the above function on a table (Person) with an ID and Name field returns ["id", ""].
This is a major SQL "injection" risk.
4

Execute this query

select * from (select "") left join my_table_to_test b on -1 = b.rowid; 

You can try it at online sqlite engine

2 Comments

this way is the best! If you dont know the name of the rowid column I suggest using: SELECT t.* FROM (SELECT 1) LEFT JOIN table AS t LIMIT 1
The suggestion by @conca led me to the solution I needed, which was to union the normal query with the "backup query for empty tables". SELECT * from 'table' UNION SELECT t.* FROM (SELECT 1) LEFT JOIN 'table' AS t;" This allows a single query to work whether the table has data or not, and I can access the column names. Thanks a lot!!!
3

The PRAGMA statement suggested by @pragmanatu works fine through any programmatic interface, too. Alternatively, the sql column of sqlite_master has the SQL statement CREATE TABLE &c &c that describes the table (but, you'd have to parse that, so I think PRAGMA table_info is more... pragmatic;-).

Comments

0

If you are suing SQLite 3.8.3 or later (supports the WITH clause), this recursive query should work for basic tables. On CTAS, YMMV.

WITH Recordify(tbl_name, Ordinal, Clause, Sql) AS ( SELECT tbl_name, 0, '', Sql FROM ( SELECT tbl_name, substr ( Sql, instr(Sql, '(') + 1, length(Sql) - instr(Sql, '(') - 1 ) || ',' Sql FROM sqlite_master WHERE type = 'table' ) UNION ALL SELECT tbl_name, Ordinal + 1, trim(substr(Sql, 1, instr(Sql, ',') - 1)), substr(Sql, instr(Sql, ',') + 1) FROM Recordify WHERE Sql > '' AND lower(trim(Sql)) NOT LIKE 'check%' AND lower(trim(Sql)) NOT LIKE 'unique%' AND lower(trim(Sql)) NOT LIKE 'primary%' AND lower(trim(Sql)) NOT LIKE 'foreign%' AND lower(trim(Sql)) NOT LIKE 'constraint%' ), -- Added to make querying a subset easier. Listing(tbl_name, Ordinal, Name, Constraints) AS ( SELECT tbl_name, Ordinal, substr(Clause, 1, instr(Clause, ' ') - 1), trim(substr(Clause, instr(Clause, ' ') + 1)) FROM Recordify WHERE Ordinal > 0 ) SELECT tbl_name, Ordinal, Name, Constraints FROM Listing ORDER BY tbl_name, lower(Name); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.