Skip to content
This repository has been archived by the owner on Apr 20, 2023. It is now read-only.

Commit

Permalink
VAX-482: Keys in electric_meta tables have to be unique
Browse files Browse the repository at this point in the history
Made table column 'key' primary key.
Added test.
Satellite schema uses default table names instead of hard-coded.
  • Loading branch information
balegas committed Jan 13, 2023
1 parent d6cb072 commit d3f4919
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 5 deletions.
15 changes: 10 additions & 5 deletions src/migrators/schema.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { satelliteDefaults } from '../satellite/config'

const { metaTable, migrationsTable, oplogTable, triggersTable } =
satelliteDefaults

export const data = {
migrations: [
{
satellite_body: [
'-- The ops log table\nCREATE TABLE IF NOT EXISTS _electric_oplog (\n rowid INTEGER PRIMARY KEY AUTOINCREMENT,\n namespace String NOT NULL,\n tablename String NOT NULL,\n optype String NOT NULL,\n primaryKey String NOT NULL,\n newRow String,\n oldRow String,\n timestamp TEXT\n);',
'-- Somewhere to keep our metadata\nCREATE TABLE IF NOT EXISTS _electric_meta (\n key TEXT,\n value BLOB\n);',
'-- Somewhere to track migrations\nCREATE TABLE IF NOT EXISTS _electric_migrations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL UNIQUE,\n sha256 TEXT NOT NULL,\n applied_at TEXT NOT NULL\n);',
"-- Initialisation of the metadata table\nINSERT INTO _electric_meta (key, value) VALUES ('compensations', 0), ('lastAckdRowId','0'), ('lastSentRowId', '0'), ('lsn', ''), ('clientId', ''), ('token', 'INITIAL_INVALID_TOKEN'), ('refreshToken', '');",
'-- These are toggles for turning the triggers on and off\nDROP TABLE IF EXISTS _electric_trigger_settings;',
`-- The ops log table\nCREATE TABLE IF NOT EXISTS ${oplogTable} (\n rowid INTEGER PRIMARY KEY AUTOINCREMENT,\n namespace String NOT NULL,\n tablename String NOT NULL,\n optype String NOT NULL,\n primaryKey String NOT NULL,\n newRow String,\n oldRow String,\n timestamp TEXT\n);`,
`-- Somewhere to keep our metadata\nCREATE TABLE IF NOT EXISTS ${metaTable} (\n key TEXT PRIMARY KEY,\n value BLOB\n);`,
`-- Somewhere to track migrations\nCREATE TABLE IF NOT EXISTS ${migrationsTable} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL UNIQUE,\n sha256 TEXT NOT NULL,\n applied_at TEXT NOT NULL\n);`,
`-- Initialisation of the metadata table\nINSERT INTO ${metaTable} (key, value) VALUES ('compensations', 0), ('lastAckdRowId','0'), ('lastSentRowId', '0'), ('lsn', ''), ('clientId', ''), ('token', 'INITIAL_INVALID_TOKEN'), ('refreshToken', '');`,
`-- These are toggles for turning the triggers on and off\nDROP TABLE IF EXISTS ${triggersTable};`,
'CREATE TABLE _electric_trigger_settings(tablename STRING PRIMARY KEY, flag INTEGER);',
],
encoding: 'escaped',
Expand Down
58 changes: 58 additions & 0 deletions test/migrators/schema.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import test from 'ava'
import Database from 'better-sqlite3'

import { rm as removeFile } from 'node:fs/promises'
import { AnyDatabase } from '../../src/drivers'

import { DatabaseAdapter } from '../../src/drivers/better-sqlite3/adapter'
import { BundleMigrator } from '../../src/migrators/bundle'
import { satelliteDefaults } from '../../src/satellite/config'

import { randomValue } from '../../src/util/random'

import { data as testMigrationsData } from '../support/migrations'
const { migrations } = testMigrationsData

type Context = {
dbName: string
adapter: DatabaseAdapter
db: AnyDatabase
}

test.beforeEach((t) => {
const dbName = `schema-migrations-${randomValue()}.db`
const db = new Database(dbName)
const adapter = new DatabaseAdapter(db)

t.context = {
adapter,
dbName,
}
})

test.afterEach.always(async (t) => {
const { dbName } = t.context as Context

await removeFile(dbName, { force: true })
await removeFile(`${dbName}-journal`, { force: true })
})

test('check schema keys are unique', async (t) => {
const { adapter } = t.context as Context

const migrator = new BundleMigrator(adapter, migrations)
await migrator.up()

await adapter.run({
sql: `INSERT INTO ${satelliteDefaults.metaTable}(key, value) values ('key', 'value')`,
})
try {
await adapter.run({
sql: `INSERT INTO ${satelliteDefaults.metaTable}(key, value) values ('key', 'value')`,
})
t.fail()
} catch (err) {
const castError = err as { code: string }
t.is(castError.code, 'SQLITE_CONSTRAINT_PRIMARYKEY')
}
})

0 comments on commit d3f4919

Please sign in to comment.