Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const tx = await db.transaction('write'); await tx.execute(...); await tx.commit
| `lower(hex(randomblob(16)))`, `hex(randomblob(n))` | `encode(gen_random_bytes(n), 'hex')` |
| `group_concat(x, sep)` | `string_agg(x::text, sep)` |
| `ifnull(a, b)` | `coalesce(a, b)` |
| `x LIKE y` | `x ILIKE y` (SQLite's LIKE is case-insensitive; `NOT LIKE` and `ESCAPE` kept) |
| `CAST(x AS INTEGER)` | `CAST(x AS BIGINT)` (SQLite's integer is 64-bit) |
| `` `backticked` `` identifiers | `"quoted"` |
| `PRAGMA ...` | no-op, empty result |
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@profullstack/libsql-pg",
"version": "0.1.3",
"version": "0.1.4",
"type": "module",
"description": "Drop-in replacement for @libsql/client that talks to Postgres, plus the tools to move a Turso/libSQL database into it: the same execute/batch/transaction surface, SQLite idioms rewritten on the way through, a schema converter and a row copier.",
"keywords": [
Expand Down
3 changes: 3 additions & 0 deletions src/rewrite.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,9 @@ export function rewriteFunctions(sql) {
return `string_agg(${distinct ? 'distinct ' : ''}(${first})::text, ${sep})`;
});
sql = replaceCode(sql, /\bifnull\s*\(/gi, 'coalesce(');
// SQLite's LIKE is case-insensitive (ASCII); Postgres's is not. ILIKE keeps the
// meaning every LIKE in a SQLite app relied on (NOT LIKE -> NOT ILIKE, ESCAPE kept).
sql = replaceCode(sql, /\blike\b/gi, 'ILIKE');
sql = replaceCalls(sql, 'instr', ({ args }) => (args.length === 2 ? `position(${args[1].trim()} in ${args[0].trim()})` : null));
sql = replaceCalls(sql, 'max', ({ args }) => (args.length >= 2 ? `greatest(${args.map((a) => a.trim()).join(', ')})` : null));
sql = replaceCalls(sql, 'min', ({ args }) => (args.length >= 2 ? `least(${args.map((a) => a.trim()).join(', ')})` : null));
Expand Down
8 changes: 6 additions & 2 deletions test/rewrite.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ describe('ids, strings and aggregates', () => {
assert.equal(sql("select group_concat(name, ' | ') from t"), "select string_agg((name)::text, ' | ') from t");
assert.equal(sql("select group_concat(distinct name, ', ') from t"), "select string_agg(distinct (name)::text, ', ') from t");
});
test('LIKE -> ILIKE (SQLite LIKE is case-insensitive), literals untouched', () => {
assert.equal(sql("select 1 from t where a like ? and b not like 'x%' escape '\\'"), "select 1 from t where a ILIKE ? and b not ILIKE 'x%' escape '\\'");
assert.equal(sql("select 'I like it' from t where c ilike ?"), "select 'I like it' from t where c ilike ?");
});
test('ifnull -> coalesce, instr -> position', () => {
assert.equal(sql('select ifnull(a, b), instr(a, b) from t'), 'select coalesce(a, b), position(b in a) from t');
});
Expand All @@ -178,9 +182,9 @@ describe('ids, strings and aggregates', () => {
test('REGEXP -> ~', () => {
assert.equal(sql("select * from t where a regexp '^x'"), "select * from t where a ~ '^x'");
});
test('LIMIT -1 -> LIMIT ALL; LIMIT ? OFFSET ? untouched; LIKE untouched', () => {
test('LIMIT -1 -> LIMIT ALL; LIMIT ? OFFSET ? untouched', () => {
assert.equal(sql('select * from t limit -1'), 'select * from t LIMIT ALL');
assert.equal(sql("select * from t where a like ? limit ? offset ?"), "select * from t where a like ? limit ? offset ?");
assert.equal(sql("select * from t where a like ? limit ? offset ?"), "select * from t where a ILIKE ? limit ? offset ?");
});
test('INDEXED BY hints are dropped', () => {
assert.equal(sql('select * from t indexed by t_idx where a = ?'), 'select * from t where a = ?');
Expand Down
Loading