diff --git a/docs/quickstart.md b/docs/quickstart.md index d4d658c..256ab33 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -54,14 +54,16 @@ my-app/ ├── pom.xml ├── sqlcj.yaml ├── sql/ -│ ├── queries.sql -│ └── schema.sql +│ ├── migrations/ +│ │ ├── V1__create_authors_and_books.sql +│ │ └── V2__add_author_created_at_and_book_index.sql +│ └── queries.sql ├── src/main/java/com/example/app/App.java └── tools/sqlcj-cli-0.1.0-SNAPSHOT.jar ``` ```bash -mkdir -p sql src/main/java/com/example/app +mkdir -p sql/migrations src/main/java/com/example/app ``` ## 4. `pom.xml` @@ -140,7 +142,7 @@ Two details matter: version: "1" sql: - name: Author - schema: sql/schema.sql + schema: sql/migrations queries: sql/queries.sql java: package: com.example.app.db @@ -157,18 +159,19 @@ relative paths above are resolved against the directory that contains the file. Generated output therefore belongs in the build directory, where `mvn clean` removes it. See [Configuration](configuration.md) for the full file format. -## 6. `sql/schema.sql` +## 6. `sql/migrations` -The schema file is a snapshot of the tables the queries use. sqlcj reads it; it -never runs it. +The configured schema is a migration directory rather than one snapshot file. +sqlcj reads its files; it never runs them. + +`sql/migrations/V1__create_authors_and_books.sql`: ```sql CREATE TABLE authors ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - bio TEXT, - created_at TIMESTAMP + id BIGSERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + bio TEXT ); CREATE TABLE books @@ -179,7 +182,29 @@ CREATE TABLE books ); ``` -The accepted column types and `CREATE TABLE` constructs are listed in +`sql/migrations/V2__add_author_created_at_and_book_index.sql`: + +```sql +ALTER TABLE authors ADD COLUMN created_at TIMESTAMP; + +CREATE INDEX books_author_id_idx ON books (author_id); +``` + +A directory named by `sql[].schema` is read in Flyway version order, so `V1` +comes before `V2` no matter how the filesystem lists the two files. The +statements of both files build one schema model: `V2` applies its +`ALTER TABLE ... ADD COLUMN` to the `authors` table `V1` created, appending +`created_at` after `bio`, and its `CREATE INDEX` is accepted and ignored, +because an index changes no column the queries can read. The modeled tables are +therefore exactly the two tables the queries use, with `authors` carrying `id`, +`name`, `bio`, and `created_at` in that order. + +See [`sql[].schema`](configuration.md#sqlschema) for the directory and ordering +rules, [Ordered Table DDL](postgresql.md#ordered-table-ddl) for the statements +that update an already modeled table, and +[Ignored Statements](postgresql.md#ignored-statements) for the statements that +are accepted without changing the model. The accepted column types and +`CREATE TABLE` constructs are listed in [PostgreSQL Support](postgresql.md). ## 7. `sql/queries.sql` @@ -381,7 +406,7 @@ One repository instance serves the whole `DataSource`-backed execution context, and each transaction constructs another repository over its caller-owned connection. No code constructs a type per query. -## 9. Start PostgreSQL and apply the schema +## 9. Start PostgreSQL and apply the migrations ```bash docker run --rm -d --name sqlcj-quickstart \ @@ -391,13 +416,17 @@ docker run --rm -d --name sqlcj-quickstart \ -p 5432:5432 \ postgres:16-alpine -docker exec -i sqlcj-quickstart psql -U quickstart -d quickstart < sql/schema.sql +docker exec -i sqlcj-quickstart psql -U quickstart -d quickstart \ + < sql/migrations/V1__create_authors_and_books.sql +docker exec -i sqlcj-quickstart psql -U quickstart -d quickstart \ + < sql/migrations/V2__add_author_created_at_and_book_index.sql ``` -sqlcj does not create or migrate tables. Applying the same snapshot text that -the compiler reads initializes the walkthrough database consistently with the -compiler's input. sqlcj never inspects the live database, so keeping the -snapshot synchronized with later database changes is your responsibility. +sqlcj reads the migration files and never runs them. Applying them is the job of +the application's migration tool, which is `psql` in the two commands above, run +in the same version order sqlcj reads them in. sqlcj never inspects the live +database, so keeping the migration directory the compiler reads and the database +the application connects to in step is your responsibility. ## 10. Generate, compile, and run @@ -493,7 +522,8 @@ That output is the whole MVP contract in one run: ## 11. Re-running after a SQL change -After editing `sql/schema.sql` or `sql/queries.sql`, repeat the same order: +After adding a migration under `sql/migrations` or editing `sql/queries.sql`, +repeat the same order: ```bash mvn clean diff --git a/examples/maven-postgresql/sql/schema.sql b/examples/maven-postgresql/sql/migrations/V1__create_authors_and_books.sql similarity index 60% rename from examples/maven-postgresql/sql/schema.sql rename to examples/maven-postgresql/sql/migrations/V1__create_authors_and_books.sql index 3ba0835..b2fabf3 100644 --- a/examples/maven-postgresql/sql/schema.sql +++ b/examples/maven-postgresql/sql/migrations/V1__create_authors_and_books.sql @@ -1,9 +1,8 @@ CREATE TABLE authors ( - id BIGSERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - bio TEXT, - created_at TIMESTAMP + id BIGSERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + bio TEXT ); CREATE TABLE books diff --git a/examples/maven-postgresql/sql/migrations/V2__add_author_created_at_and_book_index.sql b/examples/maven-postgresql/sql/migrations/V2__add_author_created_at_and_book_index.sql new file mode 100644 index 0000000..f793b17 --- /dev/null +++ b/examples/maven-postgresql/sql/migrations/V2__add_author_created_at_and_book_index.sql @@ -0,0 +1,3 @@ +ALTER TABLE authors ADD COLUMN created_at TIMESTAMP; + +CREATE INDEX books_author_id_idx ON books (author_id); diff --git a/examples/maven-postgresql/sqlcj.yaml b/examples/maven-postgresql/sqlcj.yaml index 0f9e92c..22fe534 100644 --- a/examples/maven-postgresql/sqlcj.yaml +++ b/examples/maven-postgresql/sqlcj.yaml @@ -1,7 +1,7 @@ version: "1" sql: - name: Author - schema: sql/schema.sql + schema: sql/migrations queries: sql/queries.sql java: package: com.example.app.db diff --git a/examples/maven-postgresql/src/main/java/com/example/app/App.java b/examples/maven-postgresql/src/main/java/com/example/app/App.java index c12b4b1..3cccefa 100644 --- a/examples/maven-postgresql/src/main/java/com/example/app/App.java +++ b/examples/maven-postgresql/src/main/java/com/example/app/App.java @@ -41,8 +41,9 @@ *

Every step is checked, so the process exits non-zero as soon as one * generated operation returns an unexpected result. * - *

The verification harness applies {@code sql/schema.sql} to empty - * {@code authors} and {@code books} tables before this application runs. + *

The verification harness applies the {@code sql/migrations} files in + * version order to empty {@code authors} and {@code books} tables before this + * application runs. */ public final class App { diff --git a/scripts/verify-release.sh b/scripts/verify-release.sh index b26ef2d..d056329 100755 --- a/scripts/verify-release.sh +++ b/scripts/verify-release.sh @@ -59,7 +59,7 @@ sample_mvn() { "$@" } -command -v psql >/dev/null 2>&1 || fail "psql is required to apply the sample schema" +command -v psql >/dev/null 2>&1 || fail "psql is required to apply the sample migrations" log "Staging the reactor into ${local_repo}" @@ -160,10 +160,10 @@ cat > "${regeneration_dir}/author-and-library.yaml" <<'YAML' version: "1" sql: - name: Author - schema: ../sample/sql/schema.sql + schema: ../sample/sql/migrations queries: ../sample/sql/queries.sql - name: Library - schema: ../sample/sql/schema.sql + schema: ../sample/sql/migrations queries: ../sample/sql/queries.sql java: package: com.example.app.db @@ -174,7 +174,7 @@ cat > "${regeneration_dir}/writer.yaml" <<'YAML' version: "1" sql: - name: Writer - schema: ../sample/sql/schema.sql + schema: ../sample/sql/migrations queries: ../sample/sql/queries.sql java: package: com.example.app.db @@ -230,6 +230,9 @@ for method in createAuthor getAuthor findAuthor listAuthors updateAuthorBio dele || fail "the generated repository is missing the ${method} method" done +grep -q "LocalDateTime createdAt" "${generated_package}/AuthorRepository.java" \ + || fail "the generated row is missing the column the second migration adds" + diff -r "${first_generation}" "${generated_root}" \ || fail "generating from the sample directory did not restore its first generated output" @@ -251,16 +254,17 @@ fi tr ':' '\n' < "${classpath_file}" -log "Applying the sample schema to ${jdbc_url}" +log "Applying the sample migrations to ${jdbc_url}" # psql accepts the JDBC URL as a connection URI once the 'jdbc:' prefix is -# removed, so the sample and the schema reset share one setting. +# removed, so the sample and the migration reset share one setting. PGPASSWORD="${db_password}" psql \ --set=ON_ERROR_STOP=1 \ --quiet \ --username "${db_user}" \ --command 'DROP TABLE IF EXISTS books, authors;' \ - --file "${sample_dir}/sql/schema.sql" \ + --file "${sample_dir}/sql/migrations/V1__create_authors_and_books.sql" \ + --file "${sample_dir}/sql/migrations/V2__add_author_created_at_and_book_index.sql" \ "${jdbc_url#jdbc:}" log "Compiling the sample against the staged runtime"