DEV Community

Pacharapol Withayasakpunt
Pacharapol Withayasakpunt

Posted on

What SQLite CAN actually do

Not sure about other RDBMS's.

JSON handling

This is possible via JSON1 extension. I believe the functions and syntaxes are different in PostGRES.

SELECT json_extract(col1, '$.c[2]') FROM table
WHERE json_extract(col2, '$.x') > 3;
Enter fullscreen mode Exit fullscreen mode

RegExp handling

The extension must be installed first, unlike JSON1.

I am not sure how this works for Python and Node.js, though. Maybe, this in better-sqlite3, but better-sqlite3 has a problem with Electron. This is important, because LIKE in SQLite is case-insensitive (i.e. ILIKE).

SELECT field FROM table WHERE field REGEXP '123';
Enter fullscreen mode Exit fullscreen mode

User-defined functions

I don't sure how this works either. I'd be happy to write and compile C myself.

Sort Null first, Null last

This is a built-in functionality, just like aggregate functions.

SORT BY field ASC NULLS LAST
Enter fullscreen mode Exit fullscreen mode

Add column and rename table

This is easy, as in ALTER TABLE

ALTER TABLE table_name ADD COLUMN column_definition;
Enter fullscreen mode Exit fullscreen mode

Overcoming ALTER TABLE / COLUMN limitations

This is done by

  1. Create new table
  2. Copy data
  3. Drop old table
  4. Rename new into old

So, to create a new table, you must know to get schema.

It's called PRAGMA. It generates a table. If you want to preview this in sqlite CLI, don't forget to .header on.

PRAGMA table_info(table_name);
Enter fullscreen mode Exit fullscreen mode
PRAGMA index_list(table_name);
Enter fullscreen mode Exit fullscreen mode
PRAGMA index_info(index_name);
Enter fullscreen mode Exit fullscreen mode

So, it becomes,

-- disable foreign key constraint check
PRAGMA foreign_keys=off;

-- start a transaction
BEGIN TRANSACTION;

-- Here you can drop column or rename column
CREATE TABLE IF NOT EXISTS new_table( 
   column_definition,
   ...
);
-- copy data from the table to the new_table
INSERT INTO new_table(column_list)
SELECT column_list
FROM table;

-- drop the table
DROP TABLE table;

-- rename the new_table to the table
ALTER TABLE new_table RENAME TO table; 

-- commit the transaction
COMMIT;

-- enable foreign key constraint check
PRAGMA foreign_keys=on;
Enter fullscreen mode Exit fullscreen mode

This is my implementation.

Bonus

Listing table names programmatically

There is a hidden table called sqlite_master.

SELECT [name] FROM sqlite_master
WHERE [type] = 'table' AND [name] NOT LIKE 'sqlite_%';
Enter fullscreen mode Exit fullscreen mode

Safe identifier names for tables and columns

I made a function for this, which I used in Liteorm and this new project.

/**
 * https://www.sqlite.org/lang_keywords.html
 * @param s identifier
 */
export function safeColumnName (s: string) {
  const keywords = `
    ABORT
    ACTION
    ADD
    AFTER
    ALL
    ALTER
    ALWAYS
    ANALYZE
    AND
    AS
    ASC
    ATTACH
    AUTOINCREMENT
    BEFORE
    BEGIN
    BETWEEN
    BY
    CASCADE
    CASE
    CAST
    CHECK
    COLLATE
    COLUMN
    COMMIT
    CONFLICT
    CONSTRAINT
    CREATE
    CROSS
    CURRENT
    CURRENT_DATE
    CURRENT_TIME
    CURRENT_TIMESTAMP
    DATABASE
    DEFAULT
    DEFERRABLE
    DEFERRED
    DELETE
    DESC
    DETACH
    DISTINCT
    DO
    DROP
    EACH
    ELSE
    END
    ESCAPE
    EXCEPT
    EXCLUDE
    EXCLUSIVE
    EXISTS
    EXPLAIN
    FAIL
    FILTER
    FIRST
    FOLLOWING
    FOR
    FOREIGN
    FROM
    FULL
    GENERATED
    GLOB
    GROUP
    GROUPS
    HAVING
    IF
    IGNORE
    IMMEDIATE
    IN
    INDEX
    INDEXED
    INITIALLY
    INNER
    INSERT
    INSTEAD
    INTERSECT
    INTO
    IS
    ISNULL
    JOIN
    KEY
    LAST
    LEFT
    LIKE
    LIMIT
    MATCH
    NATURAL
    NO
    NOT
    NOTHING
    NOTNULL
    NULL
    NULLS
    OF
    OFFSET
    ON
    OR
    ORDER
    OTHERS
    OUTER
    OVER
    PARTITION
    PLAN
    PRAGMA
    PRECEDING
    PRIMARY
    QUERY
    RAISE
    RANGE
    RECURSIVE
    REFERENCES
    REGEXP
    REINDEX
    RELEASE
    RENAME
    REPLACE
    RESTRICT
    RIGHT
    ROLLBACK
    ROW
    ROWS
    SAVEPOINT
    SELECT
    SET
    TABLE
    TEMP
    TEMPORARY
    THEN
    TIES
    TO
    TRANSACTION
    TRIGGER
    UNBOUNDED
    UNION
    UNIQUE
    UPDATE
    USING
    VACUUM
    VALUES
    VIEW
    VIRTUAL
    WHEN
    WHERE
    WINDOW
    WITH
    WITHOUT`
    .split('\n')
    .map((el) => el.trim())
    .filter((el) => el)

  /**
   * https://stackoverflow.com/questions/31788990/sqlite-what-are-the-restricted-characters-for-identifiers
   */
  const validIdToken = 'A-Z0-9_$:'
  const kwRegex = new RegExp(`(^|[^${validIdToken}\\)])(${keywords.join('|')})($|[^${validIdToken}\\()])`, 'gi')

  return s.replace(kwRegex, (_, p1, p2, p3) => {
    return `${p1}"${p2.replace(/"/g, '["]')}"${p3}`
  })
}
Enter fullscreen mode Exit fullscreen mode

So, I actually made a SQLite editor for nothing. Not sure about practicality. Originally made for NoSQL editor, but no offline NoSQL beats SQLite in terms of safety and functionality.

It is made to be somewhat similar to AirTable and Google Sheets; but is local and is optimized for a database.

GitHub logo patarapolw / hotcell

Yet another SQLite Editor

Top comments (1)

Collapse
 
michi profile image
Michael Z • Edited

Something I like about sqlite is that you can just have a database in-memory or in a file, that's great for running tests against. It's possible even if you actually use a different sql flavor (thanks ORMs!)