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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE TYPE book_format AS ENUM ('HARDCOVER', 'PAPERBACK', 'EBOOK');

ALTER TABLE books ADD COLUMN format book_format;

ALTER TABLE books ADD COLUMN tags TEXT[];

ALTER TABLE books ADD COLUMN details JSONB;
11 changes: 11 additions & 0 deletions examples/maven-postgresql/sql/queries.sql
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,14 @@ SELECT a.name, b.title
FROM authors a
LEFT JOIN books b ON b.author_id = a.id
ORDER BY a.id, b.id;

-- name: CreateCatalogedBook :one
INSERT INTO books (author_id, title, format, tags, details)
VALUES ($1, $2, $3, $4, $5)
RETURNING *;

-- name: ListBooksByFormat :many
SELECT *
FROM books
WHERE format = $1
ORDER BY id;
54 changes: 48 additions & 6 deletions examples/maven-postgresql/src/main/java/com/example/app/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.example.app.db.AuthorRepository;
import com.example.app.db.AuthorsRow;
import com.example.app.db.BookFormat;
import com.example.app.db.BooksRow;
import dev.sqlcj.runtime.JdbcQueryExecutor;
import dev.sqlcj.runtime.QueryCardinalityException;
import dev.sqlcj.runtime.QueryExecutor;
Expand All @@ -18,17 +20,19 @@
* Runs the documented sqlcj workflow against PostgreSQL through generated
* code: create, read, optional read, list, update, a missing-row read, a
* committed transaction, a rolled back transaction, a case-insensitive search,
* a count, a page, a book insert, a left-joined projection, and delete.
* a count, a page, a book insert, a left-joined projection, a cataloged book
* insert, a read by enum value, and delete.
*
* <p>All eleven named queries are methods of one generated
* <p>All thirteen named queries are methods of one generated
* {@link AuthorRepository}. The repository is constructed once per execution
* context: once from the {@code DataSource}-backed executor, and once more per
* transaction from a caller-owned connection.
*
* <p>Create, read, optional read, list, search, and page each return one
* complete {@code authors} row, so all six share the single top-level
* {@link AuthorsRow} record of the generated package. A query with its own
* result shape generates its own nested record:
* {@link AuthorsRow} record of the generated package, and the two queries that
* return a complete {@code books} row share the top-level {@link BooksRow}
* record. A query with its own result shape generates its own nested record:
* {@link AuthorRepository.CountAuthorsResult} carries the non-null
* {@code Long} count, and {@link AuthorRepository.ListAuthorBooksResult}
* carries an author name beside the title of the left-joined {@code books} row,
Expand All @@ -39,12 +43,18 @@
* {@code GetAuthor} query requires exactly one row and raises a
* {@link QueryCardinalityException} when none matches.
*
* <p>The third migration adds an enum, an array, and a {@code JSONB} column to
* {@code books}, so {@link BooksRow} carries a {@link BookFormat} constant of
* the generated Java enum, a {@code List<String>} of tags, and the
* {@code JSONB} document as JSON text, which PostgreSQL returns normalized.
* The enum is also bound as a query parameter to select books by format.
*
* <p>Every step is checked, so the process exits non-zero as soon as one
* generated operation returns an unexpected result.
*
* <p>The verification harness applies the {@code sql/migrations} files in
* version order to empty {@code authors} and {@code books} tables before this
* application runs.
* version order to empty {@code authors} and {@code books} tables, without the
* {@code book_format} type, before this application runs.
*/
public final class App {

Expand Down Expand Up @@ -176,6 +186,38 @@ public static void main(String[] args) throws SQLException {
+ ", " + books.get(1).name() + " / " + books.get(1).title()
);

List<String> catalogedTags = List.of("compilers", "history");
String catalogedDetails = "{\"pages\": 320}";

BooksRow cataloged = authors.createCatalogedBook(
committedId,
"Automatic Programming",
BookFormat.HARDCOVER,
catalogedTags,
catalogedDetails
);

check(cataloged != null, "CreateCatalogedBook returned no row");
check(cataloged.id() != null, "CreateCatalogedBook returned no database-generated id");
checkEquals(committedId, cataloged.authorId(), "CreateCatalogedBook author id");
checkEquals("Automatic Programming", cataloged.title(), "CreateCatalogedBook title");
checkEquals(BookFormat.HARDCOVER, cataloged.format(), "CreateCatalogedBook format");
checkEquals(catalogedTags, cataloged.tags(), "CreateCatalogedBook tags");
checkEquals(catalogedDetails, cataloged.details(), "CreateCatalogedBook details");

List<BooksRow> hardcovers = authors.listBooksByFormat(BookFormat.HARDCOVER);

checkEquals(1, hardcovers.size(), "ListBooksByFormat row count");
checkEquals(cataloged.id(), hardcovers.get(0).id(), "ListBooksByFormat id");
checkEquals(BookFormat.HARDCOVER, hardcovers.get(0).format(), "ListBooksByFormat format");
checkEquals(catalogedTags, hardcovers.get(0).tags(), "ListBooksByFormat tags");
checkEquals(catalogedDetails, hardcovers.get(0).details(), "ListBooksByFormat details");

System.out.println(
"cataloged book: " + cataloged.id() + " " + cataloged.format()
+ " " + cataloged.tags() + " " + cataloged.details()
);

int deletedRows = authors.deleteAuthor(created.id());

checkEquals(1, deletedRows, "DeleteAuthor affected rows");
Expand Down
38 changes: 24 additions & 14 deletions scripts/verify-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
# then generate the query groups 'Author' and 'Library' and next only 'Writer',
# proving that a renamed or removed entry leaves no stale repository and a
# manifest listing the surviving files alone. Both entries share the one
# top-level 'AuthorsRow' record the package generates. The final generation
# uses the sample's own 'sqlcj.yaml' from the sample directory and must restore
# the first generated output byte for byte before the sample is compiled and
# run.
# top-level 'AuthorsRow' record and the one 'BookFormat' enum the package
# generates. The final generation uses the sample's own 'sqlcj.yaml' from the
# sample directory and must restore the first generated output byte for byte
# before the sample is compiled and run. The sample's migrations declare an
# enum type and add an enum, an array, and a 'JSONB' column to 'books', so the
# generated package holds the shared 'AuthorsRow' and 'BooksRow' records, the
# 'BookFormat' enum, and one repository.
#
# Requirements: JDK 21, Maven, psql, and a reachable PostgreSQL server.
#
Expand Down Expand Up @@ -186,7 +189,7 @@ YAML
(cd "${elsewhere_dir}" && java -jar "${cli_jar}" generate \
--config ../regeneration/author-and-library.yaml)

for generated in AuthorRepository LibraryRepository AuthorsRow; do
for generated in AuthorRepository LibraryRepository AuthorsRow BooksRow BookFormat; do
[ -f "${generated_package}/${generated}.java" ] \
|| fail "the renamed-entry setup is missing ${generated_package}/${generated}.java"
done
Expand All @@ -196,6 +199,11 @@ shared_row_count=$(find "${generated_root}" -type f -name 'AuthorsRow.java' | wc
[ "${shared_row_count}" -eq 1 ] \
|| fail "expected the two entries to share 1 row record but found ${shared_row_count}"

shared_enum_count=$(find "${generated_root}" -type f -name 'BookFormat.java' | wc -l)

[ "${shared_enum_count}" -eq 1 ] \
|| fail "expected the two entries to share 1 enum but found ${shared_enum_count}"

(cd "${elsewhere_dir}" && java -jar "${cli_jar}" generate \
--config ../regeneration/writer.yaml)

Expand All @@ -207,16 +215,16 @@ done

stale_count=$(find "${generated_root}" -type f -name '*.java' | wc -l)

[ "${stale_count}" -eq 2 ] \
|| fail "expected 2 generated sources after the rename but found ${stale_count}"
[ "${stale_count}" -eq 4 ] \
|| fail "expected 4 generated sources after the rename but found ${stale_count}"

for generated in WriterRepository AuthorsRow; do
for generated in WriterRepository AuthorsRow BooksRow BookFormat; do
[ -f "${generated_package}/${generated}.java" ] \
|| fail "the surviving generated sources are missing ${generated_package}/${generated}.java"
done

expected_manifest="${workspace}/expected-manifest.txt"
printf 'com/example/app/db/AuthorsRow.java\ncom/example/app/db/WriterRepository.java\n' \
printf 'com/example/app/db/AuthorsRow.java\ncom/example/app/db/BookFormat.java\ncom/example/app/db/BooksRow.java\ncom/example/app/db/WriterRepository.java\n' \
> "${expected_manifest}"

cmp "${expected_manifest}" "${manifest_file}" \
Expand All @@ -226,18 +234,19 @@ log "Generating sources with the packaged CLI"

(cd "${sample_dir}" && java -jar "${cli_jar}" generate)

for generated in AuthorRepository AuthorsRow; do
for generated in AuthorRepository AuthorsRow BooksRow BookFormat; do
[ -f "${generated_package}/${generated}.java" ] \
|| fail "the expected generated source is missing: ${generated_package}/${generated}.java"
done

generated_count=$(find "${generated_root}" -type f -name '*.java' | wc -l)

[ "${generated_count}" -eq 2 ] \
|| fail "expected 2 generated sources but found ${generated_count}"
[ "${generated_count}" -eq 4 ] \
|| fail "expected 4 generated sources but found ${generated_count}"

for method in createAuthor getAuthor findAuthor listAuthors updateAuthorBio deleteAuthor \
searchAuthors countAuthors listAuthorPage createBook listAuthorBooks; do
searchAuthors countAuthors listAuthorPage createBook listAuthorBooks \
createCatalogedBook listBooksByFormat; do
grep -q " ${method}(" "${generated_package}/AuthorRepository.java" \
|| fail "the generated repository is missing the ${method} method"
done
Expand Down Expand Up @@ -274,9 +283,10 @@ PGPASSWORD="${db_password}" psql \
--set=ON_ERROR_STOP=1 \
--quiet \
--username "${db_user}" \
--command 'DROP TABLE IF EXISTS books, authors;' \
--command 'DROP TABLE IF EXISTS books, authors; DROP TYPE IF EXISTS book_format;' \
--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" \
--file "${sample_dir}/sql/migrations/V3__add_book_format_tags_and_details.sql" \
"${jdbc_url#jdbc:}"

log "Compiling the sample against the staged runtime"
Expand Down
Loading
Loading