diff --git a/cargo-pgx/Cargo.toml b/cargo-pgx/Cargo.toml index 88c30cd75..3018256c2 100644 --- a/cargo-pgx/Cargo.toml +++ b/cargo-pgx/Cargo.toml @@ -13,7 +13,6 @@ keywords = ["database", "postgres", "postgresql", "extension"] readme = "README.md" exclude = [ "*.png" ] - [dependencies] cargo_metadata = "0.14.1" cargo_toml = "0.11.3" diff --git a/cargo-pgx/README.md b/cargo-pgx/README.md index c6ecddb71..afc6316f3 100644 --- a/cargo-pgx/README.md +++ b/cargo-pgx/README.md @@ -20,30 +20,34 @@ As new versions of `pgx` are released, you'll want to make sure you run this com ```shell script $ cargo pgx --help -cargo-pgx +cargo-pgx 0.2.6 +ZomboDB, LLC +Cargo subcommand for 'pgx' to make Postgres extension development easy USAGE: - cargo pgx [SUBCOMMAND] + cargo pgx [OPTIONS] -FLAGS: - -h, --help Prints help information - -V, --version Prints version information +OPTIONS: + -h, --help Print help information + -v, --verbose Enable info logs, -vv for debug, -vvv for trace + -V, --version Print version information SUBCOMMANDS: - connect connect, via psql, to a Postgres instance - get get a property from the extension control file - help Prints this message or the help of the given subcommand(s) - init initialize pgx development environment for the first time - install install the extension from the current crate to the Postgres specified by whatever "pg_config" is - currently on your $PATH - new create a new extension crate - package create an installation package directory (in ./target/[debug|release]/extname-pgXX/). - run compile/install extension to a pgx-managed Postgres instance and start psql - schema generate extension schema files - start start a pgx-managed Postgres instance - status is a pgx-managed Postgres instance running? - stop stop a pgx-managed Postgres instance - test run the test suite for this crate + connect Connect, via psql, to a Postgres instance + get Get a property from the extension control file + help Print this message or the help of the given subcommand(s) + init Initize pgx development environment for the first time + install Install the extension from the current crate to the Postgres specified by + whatever `pg_config` is currently on your $PATH + new Create a new extension crate + package Create an installation package directory (in `./target/[debug|release]/extname- + pgXX/`) + run Compile/install extension to a pgx-managed Postgres instance and start psql + schema Generate extension schema files + start Start a pgx-managed Postgres instance + status Is a pgx-managed Postgres instance running? + stop Stop a pgx-managed Postgres instance + test Run the test suite for this crate ``` ## Environment Variables @@ -57,39 +61,49 @@ SUBCOMMANDS: ```shell script $ cargo pgx init - Discovered Postgres v13.3, v12.7, v11.12, v10.17 - Downloading Postgres v12.7 from https://ftp.postgresql.org/pub/source/v12.7/postgresql-12.7.tar.bz2 - Stopping Postgres v13 - Downloading Postgres v11.12 from https://ftp.postgresql.org/pub/source/v11.12/postgresql-11.12.tar.bz2 - Downloading Postgres v10.17 from https://ftp.postgresql.org/pub/source/v10.17/postgresql-10.17.tar.bz2 - Downloading Postgres v13.3 from https://ftp.postgresql.org/pub/source/v13.3/postgresql-13.3.tar.bz2 - Untarring Postgres v10.17 to /home/yourself/.pgx/10.17 - Untarring Postgres v11.12 to /home/yourself/.pgx/11.12 - Untarring Postgres v12.7 to /home/yourself/.pgx/12.7 - Untarring Postgres v13.3 to /home/yourself/.pgx/13.3 - Configuring Postgres v10.17 - Configuring Postgres v11.12 - Configuring Postgres v12.7 - Configuring Postgres v13.3 - Compiling Postgres v10.17 - Compiling Postgres v13.3 - Compiling Postgres v11.12 - Compiling Postgres v12.7 - Installing Postgres v10.17 to /home/yourself/.pgx/10.17/pgx-install - Installing Postgres v11.12 to /home/yourself/.pgx/11.12/pgx-install - Installing Postgres v12.7 to /home/yourself/.pgx/12.7/pgx-install - Installing Postgres v13.3 to /home/yourself/.pgx/13.3/pgx-install - Validating /home/yourself/.pgx/10.17/pgx-install/bin/pg_config - Validating /home/yourself/.pgx/11.12/pgx-install/bin/pg_config - Validating /home/yourself/.pgx/12.7/pgx-install/bin/pg_config - Validating /home/yourself/.pgx/13.3/pgx-install/bin/pg_config + Discovered Postgres v14.1, v13.5, v12.9, v11.14, v10.19 + Downloading Postgres v10.19 from https://ftp.postgresql.org/pub/source/v10.19/postgresql-10.19.tar.bz2 + Downloading Postgres v14.1 from https://ftp.postgresql.org/pub/source/v14.1/postgresql-14.1.tar.bz2 + Downloading Postgres v12.9 from https://ftp.postgresql.org/pub/source/v12.9/postgresql-12.9.tar.bz2 + Downloading Postgres v11.14 from https://ftp.postgresql.org/pub/source/v11.14/postgresql-11.14.tar.bz2 + Downloading Postgres v13.5 from https://ftp.postgresql.org/pub/source/v13.5/postgresql-13.5.tar.bz2 + Removing /home/yourself/.pgx/10.19 + Removing /home/yourself/.pgx/14.1 + Removing /home/yourself/.pgx/12.9 + Untarring Postgres v10.19 to /home/yourself/.pgx/10.19 + Untarring Postgres v14.1 to /home/yourself/.pgx/14.1 + Untarring Postgres v12.9 to /home/yourself/.pgx/12.9 + Removing /home/yourself/.pgx/11.14 + Untarring Postgres v11.14 to /home/yourself/.pgx/11.14 + Removing /home/yourself/.pgx/13.5 + Untarring Postgres v13.5 to /home/yourself/.pgx/13.5 + Configuring Postgres v10.19 + Configuring Postgres v12.9 + Configuring Postgres v14.1 + Configuring Postgres v11.14 + Configuring Postgres v13.5 + Compiling Postgres v10.19 + Compiling Postgres v14.1 + Compiling Postgres v12.9 + Compiling Postgres v11.14 + Compiling Postgres v13.5 + Installing Postgres v10.19 to /home/yourself/.pgx/10.19/pgx-install + Installing Postgres v11.14 to /home/yourself/.pgx/11.14/pgx-install + Installing Postgres v12.9 to /home/yourself/.pgx/12.9/pgx-install + Installing Postgres v13.5 to /home/yourself/.pgx/13.5/pgx-install + Installing Postgres v14.1 to /home/yourself/.pgx/14.1/pgx-install + Validating /home/yourself/.pgx/10.19/pgx-install/bin/pg_config + Validating /home/yourself/.pgx/11.14/pgx-install/bin/pg_config + Validating /home/yourself/.pgx/12.9/pgx-install/bin/pg_config + Validating /home/yourself/.pgx/13.5/pgx-install/bin/pg_config + Validating /home/yourself/.pgx/14.1/pgx-install/bin/pg_config ``` `cargo pgx init` is required to be run once to properly configure the `pgx` development environment. As shown by the screenshot above, it downloads the latest versions of Postgres v10, v11, v12, v13, configures them, compiles them, and installs them to `~/.pgx/`. Other `pgx` commands such as `run` and `test` will fully manage and otherwise use these Postgres installations for you. -`pgx` is designed to support multiple Postgres versions in such a way that during development, you'll know if you're trying to use a Postgres API that isn't common across all three versions. It's also designed to make testing your extension against these versions easy. This is why it requires you to have three fully compiled and installed versions of Postgres during development. +`pgx` is designed to support multiple Postgres versions in such a way that during development, you'll know if you're trying to use a Postgres API that isn't common across all versions. It's also designed to make testing your extension against these versions easy. This is why it requires you to have all fully compiled and installed versions of Postgres during development. If you want to use your operating system's package manager to install Postgres, `cargo pgx init` has optional arguments that allow you to specify where they're installed (see below). @@ -109,42 +123,35 @@ If a new minor Postgres version is released in the future you can simply run `ca ```shell script $ cargo pgx init --help -cargo-pgx-pgx-init +cargo-pgx-init 0.2.6 +ZomboDB, LLC initialize pgx development environment for the first time -USAGE: - cargo-pgx pgx init [OPTIONS] - -FLAGS: - -h, --help Prints help information - -V, --version Prints version information - -OPTIONS: -initize pgx development environment for the first time - USAGE: cargo pgx init [OPTIONS] -FLAGS: - -h, --help Prints help information - -V, --version Prints version information - OPTIONS: - --pg10 if installed locally, the path to PG10's 'pg_config' tool, or 'download' to have pgx - download/compile/install it - --pg11 if installed locally, the path to PG11's 'pg_config' tool, or 'download' to have pgx - download/compile/install it - --pg12 if installed locally, the path to PG12's 'pg_config' tool, or 'download' to have pgx - download/compile/install it - --pg13 if installed locally, the path to PG13's 'pg_config' tool, or 'download' to have pgx - download/compile/install it - --pg14 if installed locally, the path to PG14's 'pg_config' tool, or 'download' to have pgx - download/compile/install it + -h, --help Print help information + --pg10 [env: PG10_PG_CONFIG=] + --pg11 If installed locally, the path to PG11's `pgconfig` tool, or `downLoad` to + have pgx download/compile/install it [env: PG11_PG_CONFIG=] + --pg12 If installed locally, the path to PG12's `pgconfig` tool, or `downLoad` to + have pgx download/compile/install it [env: PG12_PG_CONFIG=] + --pg13 If installed locally, the path to PG13's `pgconfig` tool, or `downLoad` to + have pgx download/compile/install it [env: PG13_PG_CONFIG=] + --pg14 If installed locally, the path to PG14's `pgconfig` tool, or `downLoad` to + have pgx download/compile/install it [env: PG14_PG_CONFIG=] + -v, --verbose Enable info logs, -vv for debug, -vvv for trace + -V, --version Print version information ``` ## Creating a new Extension -![new](https://raw.githubusercontent.com/zombodb/pgx/master/cargo-pgx/new.png) +```rust +$ cargo pgx new example +$ ls example/ +Cargo.toml example.control sql src +``` `cargo pgx new ` is an easy way to get started creating a new extension. It's similar to `cargo new `, but does the additional things necessary to support building a Rust Postgres extension. @@ -154,50 +161,53 @@ If you'd like to create a "background worker" instead, specify the `--bgworker` ```shell script $ cargo pgx new --help -cargo-pgx-new 0.1.21 -create a new extension crate +cargo-pgx-new 0.2.6 +ZomboDB, LLC +Create a new extension crate USAGE: - cargo pgx new [FLAGS] - -FLAGS: - -b, --bgworker create a background worker template - -h, --help Prints help information - -V, --version Prints version information + cargo pgx new [OPTIONS] ARGS: - the name of the extension + The name of the extension + +OPTIONS: + -b, --bgworker Create a background worker template + -h, --help Print help information + -v, --verbose Enable info logs, -vv for debug, -vvv for trace + -V, --version Print version information ``` ## Managing Your Postgres Installations ```shell script -$ cargo pgx status +$ cargo pgx status all Postgres v10 is stopped Postgres v11 is stopped Postgres v12 is stopped Postgres v13 is stopped +Postgres v14 is stopped $ cargo pgx start all - Starting Postgres v10 on port 28810 - Starting Postgres v11 on port 28811 - Starting Postgres v12 on port 28812 - Starting Postgres v13 on port 28813 + Starting Postgres v10 on port 28810 + Starting Postgres v11 on port 28811 + Starting Postgres v12 on port 28812 + Starting Postgres v13 on port 28813 + Starting Postgres v14 on port 28814 -$ cargo pgx status +$ cargo pgx status all Postgres v10 is running Postgres v11 is running Postgres v12 is running Postgres v13 is running - -$ cargo pgx stop pg10 - Stopping Postgres v10 - -$ cargo pgx status -Postgres v10 is stopped -Postgres v11 is running -Postgres v12 is running -Postgres v13 is running +Postgres v14 is running + +$ cargo pgx stop all + Stopping Postgres v10 + Stopping Postgres v11 + Stopping Postgres v12 + Stopping Postgres v13 + Stopping Postgres v14 ``` `cargo pgx` has three commands for managing each Postgres installation: `start`, `stop`, and `status`. Additionally, `cargo pgx run` (see below) will automatically start its target Postgres instance if not already running. @@ -212,24 +222,30 @@ Once started, you can connect to them using `psql` (if you have it on your $PATH ```shell script $ cargo pgx run pg13 - Stopping Postgres v13 -building extension with features `pg13` -"cargo" "build" "--features" "pg13" "--no-default-features" - Finished dev [unoptimized + debuginfo] target(s) in 0.09s +building extension with features `` +"cargo" "build" + Compiling pgx-utils v0.2.6 (/home/yourself/git/zombodb/pgx/pgx-utils) + Compiling pgx-pg-sys v0.2.6 (/home/yourself/git/zombodb/pgx/pgx-pg-sys) + Compiling pgx-macros v0.2.6 (/home/yourself/git/zombodb/pgx/pgx-macros) + Compiling strings v0.1.0 (/home/yourself/git/zombodb/pgx/pgx-examples/strings) + Finished dev [unoptimized + debuginfo] target(s) in 1m 32s installing extension - Copying control file to `/home/yourself/.pgx/13.3/pgx-install/share/postgresql/extension/strings.control` - Copying shared library to `/home/yourself/.pgx/13.3/pgx-install/lib/postgresql/strings.so` - Finished dev [unoptimized + debuginfo] target(s) in 0.09s - Running `target/debug/sql-generator /home/yourself/.pgx/13.3/pgx-install/share/postgresql/extension/strings--1.0.sql` - Finished installing strings - Starting Postgres v13 on port 28813 - Re-using existing database strings -psql (13.3) + Copying control file to `/home/yourself/.pgx/13.5/pgx-install/share/postgresql/extension/strings.control` + Copying shared library to `/home/yourself/.pgx/13.5/pgx-install/lib/postgresql/strings.so` + Discovering SQL entities + Discovered 6 SQL entities: 0 schemas (0 unique), 6 functions, 0 types, 0 enums, 0 sqls, 0 ords, 0 hashes +running SQL generator +"/home/yourself/git/zombodb/pgx/pgx-examples/strings/target/debug/sql-generator" "--sql" "/home/yourself/.pgx/13.5/pgx-install/share/postgresql/extension/strings--0.1.0.sql" + Copying extension schema file to `/home/yourself/.pgx/13.5/pgx-install/share/postgresql/extension/strings--0.1.0.sql` + Finished installing strings + Starting Postgres v13 on port 28813 + Creating database strings +psql (13.5) Type "help" for help. strings=# DROP EXTENSION strings; -DROP EXTENSION +ERROR: extension "strings" does not exist strings=# CREATE EXTENSION strings; CREATE EXTENSION strings=# \df strings.* @@ -257,7 +273,7 @@ The very first time you execute `cargo pgx run pgXX`, it needs to compile not on `cargo pgx run` compiles your extension, installs it to the specified Postgres installation as described by its `pg_config` tool, starts that Postgres instance using the same process as `cargo pgx start pgXX`, and drops you into a `psql` shell connected to a database, by default, named after your extension. From there, it's up to you to create your extension and use it. -This is also the stage where `pgx` automatically generates the SQL schema for your extension. It places individual `modname.generated.sql` files into `./sql/`, and then combines those together by the order defined in `./sql/load-order.txt`. +This is also the stage where `pgx` automatically generates the SQL schema for your extension via the `sql-generator` binary. When you exit `psql`, the Postgres instance continues to run in the background. @@ -265,33 +281,36 @@ For Postgres installations which are already on your computer, `cargo pgx run` w ```shell script $ cargo pgx run --help -cargo-pgx-run -compile/install extension to a pgx-managed Postgres instance and start psql +cargo-pgx-run 0.2.6 +ZomboDB, LLC +Compile/install extension to a pgx-managed Postgres instance and start psql USAGE: - cargo pgx run [FLAGS] [OPTIONS] [--] [DBNAME] + cargo pgx run [OPTIONS] [ARGS] -FLAGS: - -h, --help Prints help information - -r, --release compile for release mode (default is debug) - -n, --no-schema Don't regenerate the schema - -V, --version Prints version information +ARGS: + Do you want to run against Postgres `pg10`, `pg11`, `pg12`, `pg13`, `pg14`? + [env: PG_VERSION=] + The database to connect to (and create if the first time). Defaults to a + database with the same name as the current extension name OPTIONS: - --features ... additional cargo features to activate (default is '--no-default-features') - -ARGS: - Do you want to run against Postgres 'pg10', 'pg11', 'pg12', 'pg13'? - The database to connect to (and create if the first time). Defaults to a database with the same - name as the current extension name + --all-features Activate all available features + --features Space-separated list of features to activate + -h, --help Print help information + -n, --no-schema Don't regenerate the schema + --no-default-features Do not activate the `default` feature + -r, --release Compile for release mode (default is debug) [env: PROFILE=] + -v, --verbose Enable info logs, -vv for debug, -vvv for trace + -V, --version Print version information ``` ## Connect to a Database ```shell script -$ cargo pgx connect pg13 strings - Re-using existing database strings -psql (13.3) +$ cargo pgx connect + Re-using existing database strings +psql (13.5) Type "help" for help. strings=# select strings.to_lowercase('PGX'); @@ -299,6 +318,8 @@ strings=# select strings.to_lowercase('PGX'); -------------- pgx (1 row) + +strings=# ``` If you'd simply like to connect to a managed version of Postgres without re-compiling and installing @@ -312,40 +333,41 @@ the specified version of Postgres isn't running, it'll be automatically started. ```shell script $ cargo pgx connect --help -cargo-pgx-connect -connect, via psql, to a Postgres instance +cargo-pgx-connect 0.2.6 +ZomboDB, LLC +Connect, via psql, to a Postgres instance USAGE: - cargo pgx connect [DBNAME] - -FLAGS: - -h, --help Prints help information - -V, --version Prints version information + cargo pgx connect [OPTIONS] [ARGS] ARGS: - Do you want to run against Postgres 'pg10', 'pg11', 'pg12', 'pg13'? - The database to connect to (and create if the first time). Defaults to a database with the same - name as the current extension name + Do you want to run against Postgres `pg10`, `pg11`, `pg12`, `pg13`, `pg14`? + [env: PG_VERSION=] + The database to connect to (and create if the first time). Defaults to a + database with the same name as the current extension name [env: DBNAME=] + +OPTIONS: + -h, --help Print help information + -v, --verbose Enable info logs, -vv for debug, -vvv for trace + -V, --version Print version information ``` ## Installing Your Extension Locally ```shell script -cargo pgx install -building extension with features `pg12` -"cargo" "build" "--features" "pg12" "--no-default-features" - Finished dev [unoptimized + debuginfo] target(s) in 0.18s +$ cargo pgx installbuilding extension with features `` +"cargo" "build" + Finished dev [unoptimized + debuginfo] target(s) in 0.06s installing extension - Copying control file to `/home/yourself/pg12/share/postgresql/extension/strings.control` - Copying shared library to `/home/yourself/pg12/lib/postgresql/strings.so` -`src/bin/sql-generator.rs` does not exist or is not what is expected. -If you encounter problems please delete it and use the generated version. -running SQL generator features `pg12` -"cargo" "run" "--bin" "sql-generator" "--features" "pg12" "--no-default-features" "--" "/home/yourself/pg12/share/postgresql/extension/strings--1.0.sql" - Finished dev [unoptimized + debuginfo] target(s) in 0.11s - Running `target/debug/sql-generator /home/yourself/pg12/share/postgresql/extension/strings--1.0.sql` - Finished installing strings + Copying control file to `/usr/share/postgresql/13/extension/strings.control` + Copying shared library to `/usr/lib/postgresql/13/lib/strings.so` + Discovering SQL entities + Discovered 6 SQL entities: 0 schemas (0 unique), 6 functions, 0 types, 0 enums, 0 sqls, 0 ords, 0 hashes +running SQL generator +"/home/yourself/git/zombodb/pgx/pgx-examples/strings/target/debug/sql-generator" "--sql" "/usr/share/postgresql/13/extension/strings--0.1.0.sql" + Copying extension schema file to `/usr/share/postgresql/13/extension/strings--0.1.0.sql` + Finished installing strings ``` If for some reason `cargo pgx run ` isn't your style, you can use `cargo pgx install` to install your extension @@ -357,56 +379,56 @@ By default, `cargo pgx install` builds your extension in debug mode. Specifying ```shell script $ cargo pgx install --help -cargo-pgx-install -install the extension from the current crate to the Postgres specified by whatever "pg_config" is currently on your -$PATH +cargo-pgx-install 0.2.6 +ZomboDB, LLC +Install the extension from the current crate to the Postgres specified by whatever `pg_config` is +currently on your $PATH USAGE: - cargo pgx install [FLAGS] [OPTIONS] - -FLAGS: - -h, --help Prints help information - -r, --release compile for release mode (default is debug) - -n, --no-schema Don't regenerate the schema - -V, --version Prints version information + cargo pgx install [OPTIONS] OPTIONS: - --features ... additional cargo features to activate (default is '--no-default-features') --c, --pg_config the `pg_config` path (default is first in $PATH) + --all-features Activate all available features + -c, --pg-config The `pg_config` path (default is first in $PATH) + --features Space-separated list of features to activate + -h, --help Print help information + --no-default-features Do not activate the `default` feature + --no-schema Don't regenerate the schema + -r, --release Compile for release mode (default is debug) [env: PROFILE=] + -v, --verbose Enable info logs, -vv for debug, -vvv for trace + -V, --version Print version information ``` ## Testing Your Extension ```shell script -$ cargo pgx test pg13 -"cargo" "test" "--all" "--features" " pg13 pg_test" "--no-default-features" - Finished test [unoptimized + debuginfo] target(s) in 0.11s - Running unittests (target/debug/deps/spi-ce9e68c581d521ac) +$ cargo pgx test +"cargo" "test" "--features" " pg_test" + Finished test [unoptimized + debuginfo] target(s) in 0.07s + Running unittests (target/debug/deps/spi-6bcc12df19bb6b9f) running 2 tests -building extension with features `pg13 pg_test` -"cargo" "build" "--features" "pg13 pg_test" "--no-default-features" - Finished dev [unoptimized + debuginfo] target(s) in 0.09s +building extension with features ` pg_test` +"cargo" "build" "--features" " pg_test" + Finished dev [unoptimized + debuginfo] target(s) in 0.06s installing extension - Copying control file to `/home/yourself/.pgx/13.3/pgx-install/share/postgresql/extension/spi.control` - Copying shared library to `/home/yourself/.pgx/13.3/pgx-install/lib/postgresql/spi.so` -`src/bin/sql-generator.rs` does not exist or is not what is expected. -If you encounter problems please delete it and use the generated version. -running SQL generator features `pg13 pg_test` -"cargo" "run" "--bin" "sql-generator" "--features" "pg13 pg_test" "--no-default-features" "--" "/home/yourself/.pgx/13.3/pgx-install/share/postgresql/extension/spi--1.0.sql" - Finished dev [unoptimized + debuginfo] target(s) in 0.09s - Running `target/debug/sql-generator /home/yourself/.pgx/13.3/pgx-install/share/postgresql/extension/spi--1.0.sql` -Aug 03 10:26:39.108 INFO Writing SQL. path=/home/yourself/.pgx/13.3/pgx-install/share/postgresql/extension/spi--1.0.sql - Finished installing spi -test tests::pg_test_spi_query_by_id_direct ... ok + Copying control file to `/home/ana/.pgx/13.5/pgx-install/share/postgresql/extension/spi.control` + Copying shared library to `/home/ana/.pgx/13.5/pgx-install/lib/postgresql/spi.so` + Discovering SQL entities + Discovered 11 SQL entities: 1 schemas (1 unique), 8 functions, 0 types, 0 enums, 2 sqls, 0 ords, 0 hashes +running SQL generator +"/home/ana/git/zombodb/pgx/pgx-examples/spi/target/debug/sql-generator" "--sql" "/home/ana/.pgx/13.5/pgx-install/share/postgresql/extension/spi--0.0.0.sql" + Copying extension schema file to `/home/ana/.pgx/13.5/pgx-install/share/postgresql/extension/spi--0.0.0.sql` + Finished installing spi test tests::pg_test_spi_query_by_id_via_spi ... ok +test tests::pg_test_spi_query_by_id_direct ... ok -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.56s +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.40s Stopping Postgres - Running unittests (target/debug/deps/sql_generator-0bc6e7d903af4637) + Running unittests (target/debug/deps/sql_generator-f6f1bc1775229242) running 0 tests @@ -432,47 +454,44 @@ make to the database are not preserved. ```shell script $ cargo pgx test --help -cargo-pgx-test -run the test suite for this crate +cargo-pgx-test 0.2.6 +ZomboDB, LLC +Run the test suite for this crate USAGE: - cargo pgx test [FLAGS] [OPTIONS] [--] [ARGS] - -FLAGS: - -h, --help Prints help information - -r, --release compile for release mode (default is debug) - -n, --no-schema Don't regenerate the schema - -V, --version Prints version information - --workspace Test all packages in the workspace - -OPTIONS: - --features ... additional cargo features to activate (default is '--no-default-features') + cargo pgx test [OPTIONS] [ARGS] ARGS: - Do you want to test for Postgres 'pg10', 'pg11', 'pg12', 'pg13', or 'all' (default)? + Do you want to run against Postgres `pg10`, `pg11`, `pg12`, `pg13`, `pg14`, + or `all`? [env: PG_VERSION=] If specified, only run tests containing this string in their names + +OPTIONS: + --all-features Activate all available features + --features Space-separated list of features to activate + -h, --help Print help information + -n, --no-schema Don't regenerate the schema + --no-default-features Do not activate the `default` feature + -r, --release compile for release mode (default is debug) [env: PROFILE=] + -v, --verbose Enable info logs, -vv for debug, -vvv for trace + -V, --version Print version information + --workspace Test all packages in the workspace ``` ## Building an Installation Package ```shell script $ cargo pgx package -building extension with features `pg12` -"cargo" "build" "--release" "--features" "pg12" "--no-default-features" +building extension with features `` +"cargo" "build" "--release" Finished release [optimized] target(s) in 0.07s installing extension - Copying control file to `target/release/spi-pg12/usr/share/postgresql/12/extension/spi.control` - Copying shared library to `target/release/spi-pg12/usr/lib/postgresql/12/lib/spi.so` - Building SQL generator with features `pg12` -"cargo" "build" "--bin" "sql-generator" "--release" "--features" "pg12" "--no-default-features" - Finished release [optimized] target(s) in 0.07s Discovering SQL entities Discovered 8 SQL entities: 0 schemas (0 unique), 6 functions, 0 types, 0 enums, 2 sqls, 0 ords, 0 hashes -running SQL generator with features `pg12` -"cargo" "run" "--bin" "sql-generator" "--release" "--features" "pg12" "--no-default-features" "--" "--sql" "/home/yourself/pgx/pgx-examples/spi/target/release/spi-pg12/usr/share/postgresql/12/extension/spi--1.0.sql" - Finished release [optimized] target(s) in 0.07s - Running `target/release/sql-generator --sql /home/yourself/pgx/pgx-examples/spi/target/release/spi-pg12/usr/share/postgresql/12/extension/spi--1.0.sql` +running SQL generator +"/home/yourself/git/zombodb/pgx/pgx-examples/spi/target/release/sql-generator" "--sql" "/home/yourself/git/zombodb/pgx/pgx-examples/spi/target/release/spi-pg13/usr/share/postgresql/13/extension/spi--0.0.0.sql" + Copying extension schema file to `target/release/spi-pg13/usr/share/postgresql/13/extension/spi--0.0.0.sql` Finished installing spi ``` @@ -493,21 +512,22 @@ distobutions or MacOS Postgres installations. ```shell script $ cargo pgx package --help - cargo-pgx-package - create an installation package directory (in ./target/[debug|release]/extname-pgXX/) for the Postgres installation - specified by whatever "pg_config" is currently on your $PATH - - USAGE: - cargo-pgx pgx package [FLAGS] +cargo-pgx-package 0.2.6 +ZomboDB, LLC +Create an installation package directory (in `./target/[debug|release]/extname-pgXX/`) - FLAGS: - -d, --debug compile for debug mode (default is release) - -h, --help Prints help information - -V, --version Prints version information +USAGE: + cargo pgx package [OPTIONS] OPTIONS: - --features ... additional cargo features to activate (default is '--no-default-features') - -c, --pg_config the `pg_config` path (default is first in $PATH) + --all-features Activate all available features + -c, --pg-config The `pg_config` path (default is first in $PATH) + -d, --debug Compile for debug mode (default is release) [env: PROFILE=] + --features Space-separated list of features to activate + -h, --help Print help information + --no-default-features Do not activate the `default` feature + -v, --verbose Enable info logs, -vv for debug, -vvv for trace + -V, --version Print version information ``` ## Inspect you Extension Schema @@ -517,35 +537,61 @@ If you just want to look at the full extension schema that pgx will generate, us ```shell script $ cargo pgx schema --help -cargo-pgx-schema 0.1.22 -generate extension schema files +cargo-pgx-schema 0.2.6 +ZomboDB, LLC +Generate extension schema files + +The SQL generation process requires configuring a few settings in the crate. Normally `cargo pgx +schema --force-default` can set these automatically. USAGE: - cargo pgx schema [FLAGS] [OPTIONS] [--] [PG_VERSION] + cargo pgx schema [OPTIONS] [PG_VERSION] -FLAGS: - -f, --force-default Force the generation of default required files - -h, --help Prints help information - -m, --manual Skip checking for required files - -r, --release Compile for release mode (default is debug) - -V, --version Prints version information - -v, --verbose Enable debug logging (-vv for trace) +ARGS: + + Do you want to run against Postgres `pg10`, `pg11`, `pg12`, `pg13`, `pg14`? OPTIONS: - -d, --dot A path to output a produced GraphViz DOT file [default: extension.dot] - --features ... additional cargo features to activate (default is none) - -o, --out A path to output a produced SQL file (default is `sql/$EXTNAME-$VERSION.sql`) - -c, --pg_config the `pg_config` path (default is first in $PATH) + --all-features + Activate all available features -ARGS: - Do you want to run against Postgres 'pg10', 'pg11', 'pg12', 'pg13'? + -c, --pg-config + The `pg_config` path (default is first in $PATH) + + -d, --dot + A path to output a produced GraphViz DOT file + + -f, --force-default + Force the generation of default required files + + --features + Space-separated list of features to activate + + -h, --help + Print help information -REQUIREMENTS - The SQL generation process requires configuring a few settings in the crate. Normally 'cargo pgx schema --force- -default' - can set these automatically. + -m, --manual + Skip checking for required files - They are documented in the README.md of cargo-pgx: https://github.com/zombodb/pgx/tree/master/cargo-pgx#Manual-SQL-Generation + --no-default-features + Do not activate the `default` feature + + -o, --out + A path to output a produced SQL file (default is `sql/$EXTNAME-$VERSION.sql`) + + -r, --release + Compile for release mode (default is debug) + + [env: PROFILE=] + + -s, --skip-build + Skip building the `sql-generator`, use an existing build + + -v, --verbose + Enable info logs, -vv for debug, -vvv for trace + + -V, --version + Print version information ``` ### Manual SQL Generation @@ -565,7 +611,7 @@ The flags are typically set by a linker script: if [[ $CARGO_BIN_NAME == "sql-generator" ]]; then UNAME=$(uname) if [[ $UNAME == "Darwin" ]]; then - TEMP=$(mktemp pgx-XXX) + TEMP=$(mktemp pgx-XXX) echo "*_pgx_internals_*" > ${TEMP} gcc -exported_symbols_list ${TEMP} $@ rm -rf ${TEMP} diff --git a/cargo-pgx/src/command/connect.rs b/cargo-pgx/src/command/connect.rs index 430b97570..bc68f25c9 100644 --- a/cargo-pgx/src/command/connect.rs +++ b/cargo-pgx/src/command/connect.rs @@ -16,7 +16,7 @@ use pgx_utils::pg_config::{PgConfig, Pgx}; pub(crate) struct Connect { /// Do you want to run against Postgres `pg10`, `pg11`, `pg12`, `pg13`, `pg14`? #[clap(env = "PG_VERSION")] - pg_version: String, + pg_version: Option, /// The database to connect to (and create if the first time). Defaults to a database with the same name as the current extension name #[clap(env = "DBNAME")] dbname: Option, @@ -26,14 +26,48 @@ pub(crate) struct Connect { impl CommandExecute for Connect { #[tracing::instrument(level = "error", skip(self))] - fn execute(self) -> eyre::Result<()> { + fn execute(mut self) -> eyre::Result<()> { + let pgx = Pgx::from_config()?; + + let pg_version = match self.pg_version { + Some(pg_version) => match pgx.get(&pg_version) { + Ok(_) => pg_version, + Err(err) => { + if self.dbname.is_some() { + return Err(err); + } + // It's actually the dbname! We should infer from the manifest. + self.dbname = Some(pg_version); + + let metadata = crate::metadata::metadata(&Default::default())?; + crate::metadata::validate(&metadata)?; + let manifest = crate::manifest::manifest(&metadata)?; + + let default_pg_version = crate::manifest::default_pg_version(&manifest) + .ok_or(eyre!("No provided `pg$VERSION` flag."))?; + default_pg_version + }, + }, + None => { + // We should infer from the manifest. + let metadata = crate::metadata::metadata(&Default::default())?; + crate::metadata::validate(&metadata)?; + let manifest = crate::manifest::manifest(&metadata)?; + + let default_pg_version = crate::manifest::default_pg_version(&manifest) + .ok_or(eyre!("No provided `pg$VERSION` flag."))?; + default_pg_version + } + }; + let dbname = match self.dbname { Some(dbname) => dbname, None => get_property("extname") .wrap_err("could not determine extension name")? .ok_or(eyre!("extname not found in control file"))?, }; - connect_psql(Pgx::from_config()?.get(&self.pg_version)?, &dbname) + + connect_psql(Pgx::from_config()?.get(&pg_version)?, &dbname) } } diff --git a/cargo-pgx/src/command/install.rs b/cargo-pgx/src/command/install.rs index db3e309f3..5246d1369 100644 --- a/cargo-pgx/src/command/install.rs +++ b/cargo-pgx/src/command/install.rs @@ -9,7 +9,7 @@ use cargo_metadata::MetadataCommand; use colored::Colorize; use eyre::{eyre, WrapErr}; use pgx_utils::get_target_dir; -use pgx_utils::pg_config::{PgConfig, Pgx}; +use pgx_utils::pg_config::PgConfig; use std::path::PathBuf; use std::process::{Command, Stdio}; @@ -35,30 +35,18 @@ pub(crate) struct Install { impl CommandExecute for Install { #[tracing::instrument(level = "error", skip(self))] fn execute(self) -> eyre::Result<()> { - let pg_config = - match std::env::var("PGX_TEST_MODE_VERSION") { - // for test mode, we want the pg_config specified in PGX_TEST_MODE_VERSION - Ok(pgver) => match Pgx::from_config()?.get(&pgver) { - Ok(pg_config) => pg_config.clone(), - Err(e) => return Err(e).wrap_err( - "PGX_TEST_MODE_VERSION does not contain a valid postgres version number", - ), - }, - - // otherwise, the user just ran "cargo pgx install", and we use whatever "pg_config" is configured - Err(_) => match self.pg_config { - None => PgConfig::from_path(), - Some(config) => PgConfig::new(PathBuf::from(config)), - }, - }; - - install_extension( - &pg_config, - self.release, - self.no_schema, - None, - &self.features, - ) + let metadata = crate::metadata::metadata(&self.features)?; + crate::metadata::validate(&metadata)?; + let manifest = crate::manifest::manifest(&metadata)?; + + let pg_config = match self.pg_config { + None => PgConfig::from_path(), + Some(config) => PgConfig::new(PathBuf::from(config)), + }; + let pg_version = format!("pg{}", pg_config.major_version()?); + let features = crate::manifest::features_for_version(self.features, &manifest, &pg_version); + + install_extension(&pg_config, self.release, self.no_schema, None, &features) } } @@ -66,6 +54,7 @@ impl CommandExecute for Install { pg_version = %pg_config.version()?, release = is_release, base_directory = tracing::field::Empty, + features = ?features.features, ))] pub(crate) fn install_extension( pg_config: &PgConfig, @@ -81,7 +70,6 @@ pub(crate) fn install_extension( ); let (control_file, extname) = find_control_file()?; - let major_version = pg_config.major_version()?; if get_property("relocatable")? != Some("false".into()) { return Err(eyre!( @@ -90,7 +78,7 @@ pub(crate) fn install_extension( )); } - build_extension(major_version, is_release, &features)?; + build_extension(is_release, &features)?; println!(); println!("installing extension"); @@ -169,37 +157,24 @@ fn copy_file(src: &PathBuf, dest: &PathBuf, msg: &str, do_filter: bool) -> eyre: } pub(crate) fn build_extension( - major_version: u16, is_release: bool, features: &clap_cargo::Features, ) -> eyre::Result<()> { - let pgx_build_features = - std::env::var("PGX_BUILD_FEATURES").unwrap_or(format!("pg{}", major_version)); let flags = std::env::var("PGX_BUILD_FLAGS").unwrap_or_default(); - let features_arg = if !features.features.is_empty() { - use std::fmt::Write; - let mut additional_features = features.features.join(" "); - let _ = write!(&mut additional_features, " {}", pgx_build_features); - additional_features - } else { - pgx_build_features - }; - let mut command = Command::new("cargo"); command.arg("build"); if is_release { command.arg("--release"); } + let features_arg = features.features.join(" "); if !features_arg.trim().is_empty() { command.arg("--features"); command.arg(&features_arg); - // While this is not 'correct' `cargo` does not let us negate features. - command.arg("--no-default-features"); } - if features.no_default_features && features_arg.trim().is_empty() { + if features.no_default_features { command.arg("--no-default-features"); } diff --git a/cargo-pgx/src/command/package.rs b/cargo-pgx/src/command/package.rs index 6b414beb0..244efbe3b 100644 --- a/cargo-pgx/src/command/package.rs +++ b/cargo-pgx/src/command/package.rs @@ -28,11 +28,18 @@ pub(crate) struct Package { impl CommandExecute for Package { #[tracing::instrument(level = "error", skip(self))] fn execute(self) -> eyre::Result<()> { + let metadata = crate::metadata::metadata(&self.features)?; + crate::metadata::validate(&metadata)?; + let manifest = crate::manifest::manifest(&metadata)?; + let pg_config = match self.pg_config { None => PgConfig::from_path(), Some(config) => PgConfig::new(PathBuf::from(config)), }; - package_extension(&pg_config, self.debug, &self.features) + let pg_version = format!("pg{}", pg_config.major_version()?); + let features = crate::manifest::features_for_version(self.features, &manifest, &pg_version); + + package_extension(&pg_config, self.debug, &features) } } diff --git a/cargo-pgx/src/command/run.rs b/cargo-pgx/src/command/run.rs index 101641ef0..de8a06b0b 100644 --- a/cargo-pgx/src/command/run.rs +++ b/cargo-pgx/src/command/run.rs @@ -20,7 +20,7 @@ use std::{os::unix::process::CommandExt, process::Command}; pub(crate) struct Run { /// Do you want to run against Postgres `pg10`, `pg11`, `pg12`, `pg13`, `pg14`? #[clap(env = "PG_VERSION")] - pg_version: String, + pg_version: Option, /// The database to connect to (and create if the first time). Defaults to a database with the same name as the current extension name dbname: Option, /// Compile for release mode (default is debug) @@ -37,18 +37,48 @@ pub(crate) struct Run { impl CommandExecute for Run { #[tracing::instrument(level = "error", skip(self))] - fn execute(self) -> eyre::Result<()> { + fn execute(mut self) -> eyre::Result<()> { + let metadata = crate::metadata::metadata(&self.features)?; + crate::metadata::validate(&metadata)?; + let manifest = crate::manifest::manifest(&metadata)?; + let pgx = Pgx::from_config()?; + + let (pg_config, pg_version) = match self.pg_version { + Some(pg_version) => { + match pgx.get(&pg_version) { + Ok(pg_config) => (pg_config, pg_version), + Err(err) => { + if self.dbname.is_some() { + return Err(err); + } + // It's actually the dbname! We should infer from the manifest. + self.dbname = Some(pg_version); + let default_pg_version = crate::manifest::default_pg_version(&manifest) + .ok_or(eyre!("No provided `pg$VERSION` flag."))?; + (pgx.get(&default_pg_version)?, default_pg_version) + }, + } + }, + None => { + // We should infer from the manifest. + let default_pg_version = crate::manifest::default_pg_version(&manifest) + .ok_or(eyre!("No provided `pg$VERSION` flag."))?; + (pgx.get(&default_pg_version)?, default_pg_version) + }, + }; + let features = crate::manifest::features_for_version(self.features, &manifest, &pg_version); + let dbname = match self.dbname { Some(dbname) => dbname, None => get_property("extname")?.ok_or(eyre!("could not determine extension name"))?, }; - + run_psql( - Pgx::from_config()?.get(&self.pg_version)?, + pg_config, &dbname, self.release, self.no_schema, - &self.features, + &features, ) } } diff --git a/cargo-pgx/src/command/schema.rs b/cargo-pgx/src/command/schema.rs index ad6a7fa1a..f78bb5d47 100644 --- a/cargo-pgx/src/command/schema.rs +++ b/cargo-pgx/src/command/schema.rs @@ -59,6 +59,10 @@ impl CommandExecute for Schema { #[tracing::instrument(level = "error", skip(self))] fn execute(self) -> eyre::Result<()> { let (_, extname) = crate::command::get::find_control_file()?; + let metadata = crate::metadata::metadata(&Default::default())?; + crate::metadata::validate(&metadata)?; + let manifest = crate::manifest::manifest(&metadata)?; + let out = match self.out { Some(out) => out, None => format!( @@ -80,30 +84,31 @@ impl CommandExecute for Schema { } }; - let pg_config = - match std::env::var("PGX_TEST_MODE_VERSION") { - // for test mode, we want the pg_config specified in PGX_TEST_MODE_VERSION - Ok(pgver) => match Pgx::from_config()?.get(&pgver) { - Ok(pg_config) => pg_config.clone(), - Err(e) => return Err(e).wrap_err( - "PGX_TEST_MODE_VERSION does not contain a valid postgres version number", - ), - }, + let (pg_config, pg_version) = match self.pg_config { + None => match self.pg_version { + None => { + let pg_version = match self.pg_version { + Some(s) => s, + None => crate::manifest::default_pg_version(&manifest) + .ok_or(eyre!("No provided `pg$VERSION` flag."))?, + }; + (Pgx::from_config()?.get(&pg_version)?.clone(), pg_version) + } + Some(pgver) => (Pgx::from_config()?.get(&pgver)?.clone(), pgver), + }, + Some(config) => { + let pg_config = PgConfig::new(PathBuf::from(config)); + let pg_version = format!("pg{}", pg_config.major_version()?); + (pg_config, pg_version) + } + }; - // otherwise, the user just ran "cargo pgx install", and we use whatever "pg_config" is configured - Err(_) => match self.pg_config { - None => match self.pg_version { - None => PgConfig::from_path(), - Some(pgver) => Pgx::from_config()?.get(&pgver)?.clone(), - }, - Some(config) => PgConfig::new(PathBuf::from(config)), - }, - }; + let features = crate::manifest::features_for_version(self.features, &manifest, &pg_version); generate_schema( &pg_config, self.release, - &self.features, + &features, &out, self.dot, log_level, @@ -132,10 +137,7 @@ pub(crate) fn generate_schema( manual: bool, skip_build: bool, ) -> eyre::Result<()> { - crate::validate::validate(&features)?; - let (control_file, _extname) = find_control_file()?; - let major_version = pg_config.major_version()?; // If not manual, we should ensure a few files exist and are what is expected. if !manual { @@ -191,17 +193,6 @@ pub(crate) fn generate_schema( )); } - let pgx_build_features = - std::env::var("PGX_BUILD_FEATURES").unwrap_or(format!("pg{}", major_version)); - let features_arg = if !features.features.is_empty() { - use std::fmt::Write; - let mut additional_features = features.features.join(" "); - let _ = write!(&mut additional_features, " {}", pgx_build_features); - additional_features - } else { - pgx_build_features - }; - let flags = std::env::var("PGX_BUILD_FLAGS").unwrap_or_default(); if !skip_build { @@ -216,14 +207,13 @@ pub(crate) fn generate_schema( command.env("RUST_LOG", log_level); } + let features_arg = features.features.join(" "); if !features_arg.trim().is_empty() { command.arg("--features"); command.arg(&features_arg); - // While this is not 'correct' `cargo` does not let us negate features. - command.arg("--no-default-features"); } - if features.no_default_features && features_arg.trim().is_empty() { + if features.no_default_features { command.arg("--no-default-features"); } diff --git a/cargo-pgx/src/command/start.rs b/cargo-pgx/src/command/start.rs index 41f1d8568..bde854269 100644 --- a/cargo-pgx/src/command/start.rs +++ b/cargo-pgx/src/command/start.rs @@ -16,7 +16,7 @@ use std::process::Stdio; pub(crate) struct Start { /// The Postgres version to start (`pg10`, `pg11`, `pg12`, `pg13`, `pg14`, or `all`) #[clap(env = "PG_VERSION")] - pg_version: String, + pg_version: Option, #[clap(from_global, parse(from_occurrences))] verbose: usize, } @@ -24,10 +24,20 @@ pub(crate) struct Start { impl CommandExecute for Start { #[tracing::instrument(level = "error", skip(self))] fn execute(self) -> eyre::Result<()> { - let pgver = self.pg_version; let pgx = Pgx::from_config()?; - for pg_config in pgx.iter(PgConfigSelector::new(&pgver)) { + let pg_version = match self.pg_version { + Some(s) => s, + None => { + let metadata = crate::metadata::metadata(&Default::default())?; + crate::metadata::validate(&metadata)?; + let manifest = crate::manifest::manifest(&metadata)?; + crate::manifest::default_pg_version(&manifest) + .ok_or(eyre!("No provided `pg$VERSION` flag."))? + } + }; + + for pg_config in pgx.iter(PgConfigSelector::new(&pg_version)) { let pg_config = pg_config?; start_postgres(pg_config)? } diff --git a/cargo-pgx/src/command/status.rs b/cargo-pgx/src/command/status.rs index 15f0cdc7d..0a312219c 100644 --- a/cargo-pgx/src/command/status.rs +++ b/cargo-pgx/src/command/status.rs @@ -14,7 +14,7 @@ use crate::CommandExecute; pub(crate) struct Status { /// The Postgres version #[clap(env = "PG_VERSION")] - pg_version: String, + pg_version: Option, #[clap(from_global, parse(from_occurrences))] verbose: usize, } @@ -22,10 +22,20 @@ pub(crate) struct Status { impl CommandExecute for Status { #[tracing::instrument(level = "error", skip(self))] fn execute(self) -> eyre::Result<()> { - let pgver = self.pg_version; let pgx = Pgx::from_config()?; - for pg_config in pgx.iter(PgConfigSelector::new(&pgver)) { + let pg_version = match self.pg_version { + Some(s) => s, + None => { + let metadata = crate::metadata::metadata(&Default::default())?; + crate::metadata::validate(&metadata)?; + let manifest = crate::manifest::manifest(&metadata)?; + crate::manifest::default_pg_version(&manifest) + .ok_or(eyre!("No provided `pg$VERSION` flag."))? + } + }; + + for pg_config in pgx.iter(PgConfigSelector::new(&pg_version)) { let pg_config = pg_config?; if status_postgres(pg_config)? { println!( diff --git a/cargo-pgx/src/command/stop.rs b/cargo-pgx/src/command/stop.rs index 9fa17a344..ad9cdab1b 100644 --- a/cargo-pgx/src/command/stop.rs +++ b/cargo-pgx/src/command/stop.rs @@ -14,7 +14,7 @@ use std::process::Stdio; pub(crate) struct Stop { /// The Postgres version to stop (`pg10`, `pg11`, `pg12`, `pg13`, `pg14`, or `all`) #[clap(env = "PG_VERSION")] - pg_version: String, + pg_version: Option, #[clap(from_global, parse(from_occurrences))] verbose: usize, } @@ -22,10 +22,20 @@ pub(crate) struct Stop { impl CommandExecute for Stop { #[tracing::instrument(level = "error", skip(self))] fn execute(self) -> eyre::Result<()> { - let pgver = self.pg_version; let pgx = Pgx::from_config()?; - for pg_config in pgx.iter(PgConfigSelector::new(&pgver)) { + let pg_version = match self.pg_version { + Some(s) => s, + None => { + let metadata = crate::metadata::metadata(&Default::default())?; + crate::metadata::validate(&metadata)?; + let manifest = crate::manifest::manifest(&metadata)?; + crate::manifest::default_pg_version(&manifest) + .ok_or(eyre!("No provided `pg$VERSION` flag."))? + } + }; + + for pg_config in pgx.iter(PgConfigSelector::new(&pg_version)) { let pg_config = pg_config?; stop_postgres(pg_config)? } diff --git a/cargo-pgx/src/command/test.rs b/cargo-pgx/src/command/test.rs index 5ae7b1686..d1cfb6279 100644 --- a/cargo-pgx/src/command/test.rs +++ b/cargo-pgx/src/command/test.rs @@ -6,7 +6,6 @@ use pgx_utils::{ get_target_dir, pg_config::{PgConfig, PgConfigSelector, Pgx}, }; -use std::fmt::Write; use std::process::{Command, Stdio}; use crate::CommandExecute; @@ -16,8 +15,8 @@ use crate::CommandExecute; #[clap(author)] pub(crate) struct Test { /// Do you want to run against Postgres `pg10`, `pg11`, `pg12`, `pg13`, `pg14`, or `all`? - #[clap(env = "PG_VERSION", default_value = "all")] - pg_version: String, + #[clap(env = "PG_VERSION")] + pg_version: Option, /// If specified, only run tests containing this string in their names testname: Option, /// compile for release mode (default is debug) @@ -29,9 +28,8 @@ pub(crate) struct Test { /// Test all packages in the workspace #[clap(long)] workspace: bool, - /// Additional cargo features to activate (default is `--no-default-features`) - #[clap(long)] - features: Vec, + #[clap(flatten)] + features: clap_cargo::Features, #[clap(from_global, parse(from_occurrences))] verbose: usize, } @@ -40,15 +38,47 @@ impl CommandExecute for Test { #[tracing::instrument(level = "error", skip(self))] fn execute(self) -> eyre::Result<()> { let pgx = Pgx::from_config()?; - for pg_config in pgx.iter(PgConfigSelector::new(&self.pg_version)) { - let pg_config = pg_config?; + let metadata = crate::metadata::metadata(&self.features)?; + crate::metadata::validate(&metadata)?; + let manifest = crate::manifest::manifest(&metadata)?; + + let pg_version = match self.pg_version { + Some(s) => s, + None => crate::manifest::default_pg_version(&manifest) + .ok_or(eyre!("No provided `pg$VERSION` flag."))?, + }; + + for pg_config in pgx.iter(PgConfigSelector::new(&pg_version)) { + let mut testname = self.testname.clone(); + let pg_config = match pg_config { + Err(error) => { + tracing::debug!( + invalid_pg_version = %pg_version, + error = %error, + "Got invalid `pg$VERSION` flag, assuming it is a testname" + ); + testname = Some(pg_version.clone()); + pgx.get( + &crate::manifest::default_pg_version(&manifest) + .ok_or(eyre!("No provided `pg$VERSION` flag."))? + )? + }, + Ok(config) => config, + }; + let pg_version = format!("pg{}", pg_config.major_version()?); + + let features = crate::manifest::features_for_version( + self.features.clone(), + &manifest, + &pg_version, + ); test_extension( pg_config, self.release, self.no_schema, self.workspace, - &self.features, - self.testname.clone(), + &features, + testname.clone(), )? } Ok(()) @@ -65,40 +95,57 @@ pub fn test_extension( is_release: bool, no_schema: bool, test_workspace: bool, - additional_features: &Vec>, + features: &clap_cargo::Features, testname: Option>, ) -> eyre::Result<()> { if let Some(ref testname) = testname { tracing::Span::current().record("testname", &tracing::field::display(&testname.as_ref())); } - - let additional_features = additional_features - .iter() - .map(AsRef::as_ref) - .collect::>(); - let major_version = pg_config.major_version()?; let target_dir = get_target_dir()?; let mut command = Command::new("cargo"); - let mut features = additional_features.join(" "); - let _ = write!(&mut features, " pg{} pg_test", major_version); + let no_default_features_arg = features.no_default_features; + let mut features_arg = features.features.join(" "); + if features.features.iter().all(|f| f != "pg_test") { + features_arg += " pg_test"; + } command .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .arg("test") - .arg("--lib") - .arg("--features") - .arg(features) - .arg("--no-default-features") .env("CARGO_TARGET_DIR", &target_dir) + .env( + "PGX_FEATURES", + features_arg.clone(), + ) + .env( + "PGX_NO_DEFAULT_FEATURES", + if no_default_features_arg { "true" } else { "false" }, + ).env( + "PGX_ALL_FEATURES", + if features.all_features { "true" } else { "false" }, + ) .env( "PGX_BUILD_PROFILE", if is_release { "release" } else { "debug" }, ) .env("PGX_NO_SCHEMA", if no_schema { "true" } else { "false" }); + if !features_arg.trim().is_empty() { + command.arg("--features"); + command.arg(&features_arg); + } + + if no_default_features_arg { + command.arg("--no-default-features"); + } + + if features.all_features { + command.arg("--all-features"); + } + if is_release { command.arg("--release"); } diff --git a/cargo-pgx/src/main.rs b/cargo-pgx/src/main.rs index eaccad0aa..154e6d445 100644 --- a/cargo-pgx/src/main.rs +++ b/cargo-pgx/src/main.rs @@ -2,7 +2,8 @@ // governed by the MIT license that can be found in the LICENSE file. mod command; -mod validate; +mod manifest; +mod metadata; use clap::Parser; use tracing_error::ErrorLayer; @@ -51,6 +52,8 @@ impl CommandExecute for CargoSubcommands { } fn main() -> color_eyre::Result<()> { + color_eyre::install()?; + let cargo_cli = CargoCommand::parse(); // Initialize tracing with tracing-error, and eyre @@ -61,7 +64,7 @@ fn main() -> color_eyre::Result<()> { Err(_) => { let log_level = match cargo_cli.verbose { 0 => "info", - 1 => "warn", + 1 => "debug", _ => "trace", }; let filter_layer = EnvFilter::new("warn"); @@ -86,7 +89,5 @@ fn main() -> color_eyre::Result<()> { .with(ErrorLayer::default()) .init(); - color_eyre::install()?; - cargo_cli.execute() } diff --git a/cargo-pgx/src/manifest.rs b/cargo-pgx/src/manifest.rs new file mode 100644 index 000000000..812d37540 --- /dev/null +++ b/cargo-pgx/src/manifest.rs @@ -0,0 +1,60 @@ +use cargo_metadata::Metadata; +use cargo_toml::Manifest; +use eyre::eyre; + +pub(crate) fn manifest(metadata: &Metadata) -> eyre::Result { + let root = metadata + .root_package() + .ok_or(eyre!("`pgx` requires a root package."))?; + let manifest = Manifest::from_path(&root.manifest_path)?; + Ok(manifest) +} + +pub(crate) fn default_pg_version(manifest: &Manifest) -> Option { + let default_features = manifest.features.get("default")?; + for default_feature in default_features { + for major_version in crate::SUPPORTED_MAJOR_VERSIONS { + let potential_feature = format!("pg{}", major_version); + if *default_feature == format!("pg{}", major_version) { + return Some(potential_feature); + } + } + } + None +} + +pub(crate) fn features_for_version( + mut features: clap_cargo::Features, + manifest: &Manifest, + pg_version: &String, +) -> clap_cargo::Features { + let default_features = manifest.features.get("default"); + + match default_features { + Some(default_features) => { + if default_features.contains(&pg_version) { + return features; + } + let default_features = default_features + .iter() + .filter(|default_feature| { + for supported_major in crate::SUPPORTED_MAJOR_VERSIONS { + if **default_feature == format!("pg{}", supported_major) { + return false; + } + } + true + }) + .cloned() + .collect::>(); + features.no_default_features = true; + features.features.extend(default_features); + if features.features.iter().all(|f| f != pg_version) { + features.features.push(pg_version.clone()); + } + } + None => (), + }; + + features +} diff --git a/cargo-pgx/src/validate.rs b/cargo-pgx/src/metadata.rs similarity index 86% rename from cargo-pgx/src/validate.rs rename to cargo-pgx/src/metadata.rs index f419a4990..ec2713f34 100644 --- a/cargo-pgx/src/validate.rs +++ b/cargo-pgx/src/metadata.rs @@ -1,13 +1,16 @@ -use cargo_metadata::MetadataCommand; -use semver::{Version, VersionReq}; +use cargo_metadata::{Metadata, MetadataCommand}; use eyre::eyre; +use semver::{Version, VersionReq}; -#[tracing::instrument(level = "error", fields(features = ?features.features))] -pub fn validate(features: &clap_cargo::Features) -> eyre::Result<()> { +pub fn metadata(features: &clap_cargo::Features) -> eyre::Result { let mut metadata_command = MetadataCommand::new(); features.forward_metadata(&mut metadata_command); let metadata = metadata_command.exec()?; + Ok(metadata) +} +#[tracing::instrument(level = "error", skip_all)] +pub fn validate(metadata: &Metadata) -> eyre::Result<()> { let cargo_pgx_version = env!("CARGO_PKG_VERSION"); let cargo_pgx_version_req = VersionReq::parse(&format!("~{}", cargo_pgx_version))?; diff --git a/pgx-tests/src/framework.rs b/pgx-tests/src/framework.rs index 2ab743fba..00fd6e449 100644 --- a/pgx-tests/src/framework.rs +++ b/pgx-tests/src/framework.rs @@ -235,26 +235,40 @@ fn install_extension() -> eyre::Result<()> { eprintln!("installing extension"); let is_release = std::env::var("PGX_BUILD_PROFILE").unwrap_or("debug".into()) == "release"; let no_schema = std::env::var("PGX_NO_SCHEMA").unwrap_or("false".into()) == "true"; + let mut features = std::env::var("PGX_FEATURES").unwrap_or("".to_string()); + if !features.contains("pg_test") { + features += " pg_test"; + } + let no_default_features = std::env::var("PGX_NO_DEFAULT_FEATURES").unwrap_or("false".to_string()) == "true"; + let all_features = std::env::var("PGX_ALL_FEATURES").unwrap_or("false".to_string()) == "true"; + + let pg_version = format!("pg{}", pg_sys::get_pg_major_version_string()); + let pgx = Pgx::from_config()?; + let pg_config = pgx.get(&pg_version)?; let mut command = Command::new("cargo"); command .arg("pgx") .arg("install") + .arg("--pg-config") + .arg(pg_config.path().ok_or(eyre!("No pg_config found"))?) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) - .env( - "PGX_TEST_MODE_VERSION", - format!("pg{}", pg_sys::get_pg_major_version_string()), - ) - .env("CARGO_TARGET_DIR", get_target_dir()?) - .env( - "PGX_BUILD_FEATURES", - format!( - "pg{} pg_test", - pg_sys::get_pg_major_version_string().to_string() - ), - ); + .env("CARGO_TARGET_DIR", get_target_dir()?); + if !features.trim().is_empty() { + command.arg("--features"); + command.arg(features); + } + + if no_default_features { + command.arg("--no-default-features"); + } + + if all_features { + command.arg("--all-features"); + } + if is_release { command.arg("--release"); }