From a0c463b99824eb8e7cfc68a3bc2bfb36919a4bbe Mon Sep 17 00:00:00 2001 From: Laurence Isla Date: Tue, 23 Aug 2022 21:59:48 -0500 Subject: [PATCH] Add missing changes/fixes/features for v10 --- docs/api.rst | 290 +++++++++++---- docs/configuration.rst | 28 +- .../working-with-postgresql-data-types.rst | 171 ++++----- docs/index.rst | 2 +- docs/install.rst | 2 +- docs/releases/latest.rst | 143 -------- docs/releases/v10.0.0.rst | 337 ++++++++++++++++++ postgrest.dict | 5 + 8 files changed, 676 insertions(+), 302 deletions(-) delete mode 100644 docs/releases/latest.rst create mode 100644 docs/releases/v10.0.0.rst diff --git a/docs/api.rst b/docs/api.rst index 31f627bb9..50d78aec4 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -801,6 +801,7 @@ The current possibilities are: * ``text/csv`` * ``application/json`` * ``application/openapi+json`` +* ``application/geo+json`` and in the special case of a single-column select the following additional three formats; also see the section :ref:`scalar_return_formats`: @@ -870,35 +871,56 @@ In addition to providing RESTful routes for each table and view, PostgREST allow API call. This reduces the need for multiple API requests. The server uses **foreign keys** to determine which tables and views can be returned together. For example, consider a database of films and their awards: -.. important:: - - PostgREST needs `FOREIGN KEY constraints `_ to be able to do Resource Embedding. - .. image:: _static/film.png -As seen above in :ref:`v_filter` we can request the titles of all films like this: +.. important:: + + * PostgREST needs `FOREIGN KEY constraints `_ to be able to do Resource Embedding. + * Whenever FOREIGN KEY constraints change in the database schema you must refresh PostgREST's schema cache for Resource Embedding to work properly. See the section :ref:`schema_reloading`. + +.. _one-to-many: + +One-to-many relationships +------------------------- + +When a one-to-many relationship is detected, the embedded resource is returned as a JSON array. For example, we can request the Directors and the Films they directed because there is a foreign key constraint between them, like this: .. tabs:: .. code-tab:: http - GET /films?select=title HTTP/1.1 + GET /directors?select=last_name,films(title) HTTP/1.1 .. code-tab:: bash Curl - curl "http://localhost:3000/films?select=title" - -This might return something like + curl "http://localhost:3000/directors?select=last_name,films(title)" .. code-block:: json [ - { "title": "Workers Leaving The Lumière Factory In Lyon" }, - { "title": "The Dickson Experimental Sound Film" }, - { "title": "The Haunted Castle" } + { "last_name": "Lumière", + "films": [ + {"title": "Workers Leaving The Lumière Factory In Lyon"} + ] + }, + { "last_name": "Dickson", + "films": [ + {"title": "The Dickson Experimental Sound Film"} + ] + }, + { "last_name": "Méliès", + "films": [ + {"title": "The Haunted Castle"} + ] + } ] -However because a foreign key constraint exists between Films and Directors, we can request this information be included: +.. _many-to-one: + +Many-to-one relationships +------------------------- + +When a many-to-one relationship is detected, the embedded resource is returned as a JSON object. For example, we can request all the Films and the Director for each film like this: .. tabs:: @@ -910,8 +932,6 @@ However because a foreign key constraint exists between Films and Directors, we curl "http://localhost:3000/films?select=title,directors(id,last_name)" -Which would return - .. code-block:: json [ @@ -935,10 +955,7 @@ Which would return } ] -In this example, since the relationship is a forward relationship, there is -only one director associated with a film. As the table name is plural it might -be preferable for it to be singular instead. An table name alias can accomplish -this: +However, the table name is in plural, which is not accurate since a Film is directed by only one Director. Using a table name alias can solve this: .. tabs:: @@ -950,34 +967,31 @@ this: curl "http://localhost:3000/films?select=title,director:directors(id,last_name)" -.. important:: +.. _many-to-many: - Whenever FOREIGN KEY constraints change in the database schema you must refresh PostgREST's schema cache for Resource Embedding to work properly. See the section :ref:`schema_reloading`. - -Embedding through join tables ------------------------------ +Many-to-many relationships +-------------------------- PostgREST can also detect many-to-many relationships going through join tables. For this, the join table must contain foreign keys to the tables in -the many-to-many relationship and its primary key must include these foreign key columns. +the many-to-many relationship and its composite primary key must include these foreign key columns. .. code-block:: postgresql - create table "Roles"( - film_id int references "Films"(id) - , actor_id int references "Actors"(id) + create table roles( + film_id int references films(id) + , actor_id int references actors(id) , primary key(film_id, actor_id) - ) + ); -- the many-to-many relationship can also be detected if the join table has a surrogate key, -- as long as the foreign key columns are also part of the primary key - create table "Roles"( + create table roles( id int generated always as identity, - , film_id int references "Films"(id) - , actor_id int references "Actors"(id) + , film_id int references films(id) + , actor_id int references actors(id) , primary key(id, film_id, actor_id) - ) - + ); Then you can request the Actors for Films (which in this case finds the information through Roles). @@ -991,6 +1005,84 @@ Then you can request the Actors for Films (which in this case finds the informat curl "http://localhost:3000/actors?select=films(title,year)" +.. _one-to-one: + +One-to-one relationships +------------------------ + +PostgREST detects one-to-one relationships when a foreign key is also the primary key of the table or when the foreign key has a ``UNIQUE`` constraint. + +.. code-block:: postgresql + + -- references Films using the primary key as a foreign key + CREATE TABLE technical_specs( + film_id INT PRIMARY KEY REFERENCES films, + runtime TIME, + camera TEXT, + sound TEXT + ); + + -- references Films using a foreign key with unique constraint + CREATE TABLE technical_specs( + film_id INT REFERENCES films UNIQUE, + runtime TIME, + camera TEXT, + sound TEXT + ); + +Now, the embedding between Films and Technical_Specs is returned as a JSON object no matter the order. + +.. tabs:: + + .. code-tab:: http + + GET /films?select=title,technical_specs(*) HTTP/1.1 + + .. code-tab:: bash Curl + + curl "http://localhost:3000/films?select=title,technical_specs(*)" + +.. _computed_relationships: + +Computed Relationships +---------------------- + +You can customize how PostgREST detects relationships between two tables. To do this, you need to create a function that has one of the tables as a single parameter and the other as its return type. For instance: + +.. code-block:: postgres + + CREATE FUNCTION director_competition(directors) RETURNS SETOF competitions AS $$ + SELECT c.* + FROM competitions c + JOIN nominations n ON c.id = n.competition_id + JOIN films f ON n.film_id = f.id + WHERE f.director_id = $1.id + $$ STABLE LANGUAGE sql; + +The above function allows a direct relationship between ``directors`` and ``competitions``: + +.. tabs:: + + .. code-tab:: http + + GET /directors?select=*,competitions:director_competition(name) HTTP/1.1 + + .. code-tab:: bash Curl + + curl "http://localhost:3000/directors?select=*,competitions:director_competition(name)" + +Take into consideration that the opposite relationship will not be detected, so you need to create another function for that. + +Computed relationships also allow you to override the ones that are detected by default. For example, this function can change the ``/films?select=directors(*)`` embedding: + +.. code-block:: postgres + + CREATE FUNCTION directors(films) RETURNS SETOF directors ROW 1 AS $$ + -- Override the relationship here + $$ STABLE LANGUAGE sql; + +Note that if ``ROW 1`` is added, PostgREST will detect a :ref:`many-to-one relationship ` and return a JSON object instead of an array embedding. + .. _nested_embedding: Nested Embedding @@ -1460,25 +1552,24 @@ Hint Disambiguation If specifying the **target** is not enough for unambiguous embedding, you can add a **hint**. For example, let's assume we create two views of ``addresses``: ``central_addresses`` and ``eastern_addresses``. -Since PostgREST supports :ref:`embedding_views` by detecting **source foreign keys** in the views, embedding with the foreign key -as the **target** will not be enough for an unambiguous embed: +PostgREST cannot detect a view as an embedded resource by using a column name or foreign key name as targets, that is why we need to use the view name ``central_addresses`` instead. But, still, this is not enough for an unambiguous embed. .. tabs:: .. code-tab:: http - GET /orders?select=*,billing_address(*) HTTP/1.1 + GET /orders?select=*,central_addresses(*) HTTP/1.1 .. code-tab:: bash Curl - curl "http://localhost:3000/orders?select=*,billing_address(*)" -i + curl "http://localhost:3000/orders?select=*,central_addresses(*)" -i .. code-block:: http HTTP/1.1 300 Multiple Choices For solving this case, in addition to the **target**, we can add a **hint**. -Here we specify ``central_addresses`` as the **target** and the ``billing_address`` foreign key as the **hint**: +Here, we still specify ``central_addresses`` as the **target** and use the ``billing_address`` foreign key as the **hint**: .. tabs:: @@ -1510,6 +1601,10 @@ Hints also work alongside ``!inner`` if a top level filtering is needed. From th curl "http://localhost:3000/orders?select=*,central_addresses!billing_address!inner(*)¢ral_addresses.code=AB1000" +.. note:: + + If the relationship is so complex that hint disambiguation does not solve it, then using :ref:`computed_relationships` is the best alternative. + .. _insert: Insertions @@ -1695,39 +1790,6 @@ Doing a full table update without filters is not allowed and will result in 0 up Updates also support :code:`Prefer: return=representation` plus :ref:`v_filter`. -.. _bulk_update: - -Bulk Update ------------ - -You can update rows with different data by providing a JSON array of objects having uniform keys, the rows will be chosen based on the primary key column(s) values. - -.. tabs:: - - .. code-tab:: http - - PATCH /employees HTTP/1.1 - - [ - { "id": 1, "name": "Renamed employee 1", "salary": 40000 }, - { "id": 2, "name": "Renamed employee 2", "salary": 52000 }, - { "id": 3, "name": "Renamed employee 3", "salary": 60000 } - ] - - .. code-tab:: bash Curl - - curl "http://localhost:3000/employees" \ - -X PATCH -H "Content-Type: application/json" \ - -d @- << EOF - [ - { "id": 1, "name": "Renamed employee 1", "salary": 40000 }, - { "id": 2, "name": "Renamed employee 2", "salary": 52000 }, - { "id": 3, "name": "Renamed employee 3", "salary": 60000 } - ] - EOF - -You must not include any filters for this to work. If you provide filters, only the values of the first object in the array will be used for the update. - .. _upsert: Upsert @@ -2497,6 +2559,8 @@ Also if you wish to generate a ``summary`` field you can do it by having a multi spans multiple lines$$; +If you need to include the ``security`` and ``securityDefinitions`` options, set the :ref:`openapi-security-active` configuration to ``true``. + You can use a tool like `Swagger UI `_ to create beautiful documentation from the description and to host an interactive web-based dashboard. The dashboard allows developers to make requests against a live PostgREST server, and provides guidance with request headers and example request bodies. .. important:: @@ -2550,7 +2614,7 @@ For a view, the methods are determined by the presence of INSTEAD OF TRIGGERS. | `auto-updatable views `_ | +--------------------+-------------------------------------------------------------------------------------------------+ -For functions, OPTIONS requests are not supported. +For functions, the methods depend on their volatility. ``VOLATILE`` functions allow only ``OPTIONS,POST``, whereas the rest also permit ``GET,HEAD``. .. important:: @@ -2817,3 +2881,83 @@ Returns: "hint": "Upgrade your plan", "code": "PT402" } + +.. _explain_plan: + +Execution plan +-------------- + +You can get the execution plan of a request by adding the ``Accept: application/vnd.pgrst.plan`` header after setting the :ref:`db-plan-enabled` configuration to ``true``. It is useful to verify why a certain operation might be expensive as a result of using `EXPLAIN `_ on the generated query for the request. + +The output of the plan is generated in ``text`` format by default: + +.. tabs:: + + .. code-tab:: http + + GET /users?select=name&order=id HTTP/1.1 + Accept: application/vnd.pgrst.plan + + .. code-tab:: bash Curl + + curl "http://localhost:3000/users?select=name&order=id" \ + -H "Accept: application/vnd.pgrst.plan" + +.. code-block:: psql + + Aggregate (cost=73.65..73.68 rows=1 width=112) + -> Index Scan using users_pkey on users (cost=0.15..60.90 rows=850 width=36) + +The same execution can be returned in ``json`` format by using the ``Accept: application/vnd.pgrst.plan+json`` header instead: + +.. tabs:: + + .. code-tab:: http + + GET /users?select=name&order=id HTTP/1.1 + Accept: application/vnd.pgrst.plan+json + + .. code-tab:: bash Curl + + curl "http://localhost:3000/users?select=name&order=id" \ + -H "Accept: application/vnd.pgrst.plan+json" + +.. code-block:: json + + [ + { + "Plan": { + "Node Type": "Aggregate", + "Strategy": "Plain", + "Partial Mode": "Simple", + "Parallel Aware": false, + "Async Capable": false, + "Startup Cost": 73.65, + "Total Cost": 73.68, + "Plan Rows": 1, + "Plan Width": 112, + "Plans": [ + { + "Node Type": "Index Scan", + "Parent Relationship": "Outer", + "Parallel Aware": false, + "Async Capable": false, + "Scan Direction": "Forward", + "Index Name": "users_pkey", + "Relation Name": "users", + "Alias": "users", + "Startup Cost": 0.15, + "Total Cost": 60.90, + "Plan Rows": 850, + "Plan Width": 36 + } + ] + } + } + ] + +You can also get the result plan of the different media types that PostgREST supports by adding them to the header using ``for``. For instance, to obtain the plan for a :ref:`text/xml ` media type in json format, you need to add the ``Accept: application/vnd.pgrst.plan; for=text/xml`` header. + +Additionally, the deactivated parameters of the ``EXPLAIN`` command can be enabled by adding them to the header using ``options``. The available parameters are ``analyze``, ``verbose``, ``settings``, ``buffers`` and ``wal``, while the remaining ones are active by default. For example, to add the ``analyze`` and ``wal`` parameters, add the ``Accept: application/vnd.pgrst.plan; options=analyze|wal`` header. + +Note that any changes done will be committed when activating the ``analyze`` option. To avoid this, set the :ref:`db-tx-end` configuration in a way that allows to rollback the changes according to your preference. diff --git a/docs/configuration.rst b/docs/configuration.rst index 66061904b..1e45e1065 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -154,8 +154,9 @@ db-channel-enabled Boolean True Y db-config Boolean True Y db-extra-search-path String public Y db-max-rows Int ∞ Y +db-plan-enabled Boolean False Y db-pool Int 10 -db-pool-timeout Int 10 +db-pool-timeout Int 3600 db-pre-request String Y db-prepared-statements Boolean True Y db-schemas String public Y @@ -168,6 +169,7 @@ jwt-secret String Y jwt-secret-is-base64 Boolean False Y log-level String error Y openapi-mode String follow-privileges Y +openapi-security-active Boolean False Y openapi-server-proxy-uri String Y raw-media-types String Y server-host String !4 @@ -282,6 +284,18 @@ db-max-rows A hard limit to the number of rows PostgREST will fetch from a view, table, or stored procedure. Limits payload size for accidental or malicious requests. +.. _db-plan-enabled: + +db-plan-enabled +--------------- + + =============== ===================== + **Environment** PGRST_DB_PLAN_ENABLED + **In-Database** pgrst.db_plan_enabled + =============== ===================== + + When this is set to :code:`true`, the execution plan of a request can be retrieved by using the :code:`Accept: application/vnd.pgrst.plan` header. See :ref:`explain_plan`. + .. _db-pool: db-pool @@ -551,6 +565,18 @@ openapi-mode # Throws a `404 Not Found` error when accessing the API root path openapi-mode = "disabled" +.. _openapi-security-active: + +openapi-security-active +----------------------- + + =============== ============================= + **Environment** PGRST_OPENAPI_SECURITY_ACTIVE + **In-Database** pgrst.openapi_security_active + =============== ============================= + +When this is set to :code:`true`, security options are included in the :ref:`OpenAPI output `. + .. _openapi-server-proxy-uri: openapi-server-proxy-uri diff --git a/docs/how-tos/working-with-postgresql-data-types.rst b/docs/how-tos/working-with-postgresql-data-types.rst index 9ad0c934d..fb317c97e 100644 --- a/docs/how-tos/working-with-postgresql-data-types.rst +++ b/docs/how-tos/working-with-postgresql-data-types.rst @@ -597,6 +597,8 @@ You can also query and filter the value of a ``hstore`` column using the arrow o [{ "native": "مصر" }] +.. _ww_postgis: + PostGIS ------- @@ -638,76 +640,81 @@ Say you want to add areas in polygon format. The request using string representa ] EOF -Now, when you request the information, PostgREST will automatically cast the ``area`` column to ``JSON`` format. Although this output is useful, you will want to use the PostGIS functions to have more control on filters or casts. For these cases, creating a ``function`` is your best option. For example, let's use some of the functions to get the data in `GeoJSON format `_ and to calculate the area in square units: - -.. code-block:: postgres - - create or replace function coverage_geo(filter text) returns json as $$ - select - json_build_object( - 'name', c.name, - -- Get the Geometry Object - 'geo_geometry', st_AsGeoJSON(c.area)::json, - -- Get the Feature Object - 'geo_feature', st_AsGeoJSON(c.*)::json, - -- Calculate the area in square units - 'square_units', st_area(c.area) - ) - from coverage c - where c.name = filter; - $$ language sql; - - -- Create another function for the FeatureCollection Object - -- for the sake of making the examples clearer - create or replace function coverage_geo_collection() returns json as $$ - select - json_build_object( - 'type', 'FeatureCollection', - 'features', json_agg(st_AsGeoJSON(c.*)::json) - ) - as geo_feature_collection - from coverage c; - $$ language sql; - -Now the query will return the information as you expected: +Now, when you request the information, PostgREST will automatically cast the ``area`` column into a ``Polygon`` geometry type. Although this is useful, you may need the whole output to be in `GeoJSON `_ format out of the box, which can be done by including the ``Accept: application/geo+json`` in the request. This will work for PostGIS versions 3.0.0 and up and will return the output as a `FeatureCollection Object `_: .. tabs:: .. code-tab:: http - GET /rpc/coverage_geo?filter=big HTTP/1.1 + GET /coverage HTTP/1.1 + Accept: application/geo+json .. code-tab:: bash Curl - curl "http://localhost:3000/rpc/coverage_geo?filter=big" + curl "http://localhost:3000/coverage" \ + -H "Accept: application/geo+json" .. code-block:: json { - "name": "big", - "geo_geometry": { - "type": "Polygon", - "coordinates": [ - [[0,0],[10,0],[10,10],[0,10],[0,0]] - ] - }, - "geo_feature": { - "type": "Feature", - "geometry": { - "type": "Polygon", - "coordinates": [ - [[0,0],[10,0],[10,10],[0,10],[0,0]] - ] + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [[0,0],[1,0],[1,1],[0,1],[0,0]] + ] + }, + "properties": { + "id": 1, + "name": "small" + } }, - "properties": { - "id": 2, - "name": "big" + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [[0,0],[10,0],[10,10],[0,10],[0,0]] + ] + }, + "properties": { + "id": 2, + "name": "big" + } } - }, - "square_units": 100 + ] } -And for the Feature Collection format: +If you need to add an extra property, like the area in square units by using ``st_area(area)``, you could add a generated column to the table and it will appear in the ``properties`` key of each ``Feature``. + +.. code-block:: postgres + + alter table coverage + add square_units double precision generated always as ( st_area(area) ) stored; + +In the case that you are using older PostGIS versions, then creating a function is your best option. For example: + +.. code-block:: postgres + + create or replace function coverage_geo_collection() returns json as $$ + select + json_build_object( + 'type', 'FeatureCollection', + 'features', json_agg( + json_build_object( + 'type', 'Feature', + 'geometry', st_AsGeoJSON(c.area)::json, + 'properties', json_build_object('id', c.id, 'name', c.name) + ) + ) + ) + from coverage c; + $$ language sql; + +Now this query will return the same results: .. tabs:: @@ -722,35 +729,33 @@ And for the Feature Collection format: .. code-block:: json { - "geo_feature_collection": { - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "geometry": { - "type": "Polygon", - "coordinates": [ - [[0,0],[1,0],[1,1],[0,1],[0,0]] - ] - }, - "properties": { - "id": 1, - "name": "small" - } + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [[0,0],[1,0],[1,1],[0,1],[0,0]] + ] }, - { - "type": "Feature", - "geometry": { - "type": "Polygon", - "coordinates": [ - [[0,0],[10,0],[10,10],[0,10],[0,0]] - ] - }, - "properties": { - "id": 2, - "name": "big" - } + "properties": { + "id": 1, + "name": "small" } - ] - } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [[0,0],[10,0],[10,10],[0,10],[0,0]] + ] + }, + "properties": { + "id": 2, + "name": "big" + } + } + ] } diff --git a/docs/index.rst b/docs/index.rst index 989c55f16..dcb1089fd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -98,7 +98,7 @@ The project has a friendly and growing community. Join our `chat room + v10.0.0 v9.0.1 v9.0.0 releases/v8.0.0 diff --git a/docs/install.rst b/docs/install.rst index c68ae1586..ec3c97721 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -228,7 +228,7 @@ When a pre-built binary does not exist for your system you can build the project You can build PostgREST from source with `Stack `_. It will install any necessary Haskell dependencies on your system. -* `Install Stack `_ for your platform +* `Install Stack `_ for your platform * Install Library Dependencies ===================== ======================================= diff --git a/docs/releases/latest.rst b/docs/releases/latest.rst deleted file mode 100644 index 3b7eaaf97..000000000 --- a/docs/releases/latest.rst +++ /dev/null @@ -1,143 +0,0 @@ - -Latest -====== - -These are features/bugfixes not yet on a stable version. You can try them by downloading the latest pre-releases `on the GitHub release page `_. - -Features --------- - -API -~~~ - -Bulk Update -^^^^^^^^^^^ - -See :ref:`bulk_update`. - -Access Composite Type fields and Array elements -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can now :ref:`access fields of a Composite type or elements of an Array type ` with the arrow operators(``->``, ``->>``) in the same way you would access the JSON type fields. - -Improved Error Messages -^^^^^^^^^^^^^^^^^^^^^^^ - -To increase consistency, all the errors messages are now normalized. The ``hint``, ``details``, ``code`` and ``message`` fields will always be present in the body, each one defaulting to a -``null`` value. In the same way, the :ref:`errors that were raised ` with ``SQLSTATE`` now include the ``message`` and ``code`` in the body. - -In addition to these changes and to further clarify the source of an error, PostgREST now adds a ``PGRST`` prefix to the error code of all the errors that are PostgREST-specific and don't come from the database. These errors have a unique code that identifies them and are documented in the :ref:`pgrst_errors` section. - -Alongside these changes, there is now a dedicated reference page for :doc:`Error documentation `. - -Administration -~~~~~~~~~~~~~~ - -Health checks -^^^^^^^^^^^^^ - -Admins can now benefit from two :ref:`health check endpoints ` exposed in a different port than the main app. When activated, the ``live`` and ``ready`` endpoints are available to verify if PostgREST is alive and running or if the database connection and the :ref:`schema cache ` are ready for querying. - -Logging users -^^^^^^^^^^^^^ - -You can now verify the current authenticated database user in the :ref:`request log ` on stdout. - -Run without configuration -^^^^^^^^^^^^^^^^^^^^^^^^^ - -It is now possible to execute PostgREST without specifying any configuration variable, even without the three that were mandatory - - - If :ref:`db-uri` is not set, PostgREST will use the `libpq environment variables `_ for the database connection. - - If :ref:`db-schemas` is not set, it will use the database ``public`` schema. - - If :ref:`db-anon-role` is not set, it will not allow anonymous requests. - -Documentation improvements -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* Added a :doc:`/how-tos/working-with-postgresql-data-types` how-to, which contains explanations and examples on how to work with different PostgreSQL data types such as timestamps, ranges or PostGIS types, among others. - -* Added in-database and environment variable settings for each :ref:`configuration variable `. - -* Added the :ref:`file_descriptors` subsection. - -* Moved the :ref:`error_source` and the :ref:`status_codes` sections to the :doc:`errors reference page `. - -* Moved the *Casting type to custom JSON* how-to to the :ref:`casting_range_to_json` subsection. - -* Removed direct links for PostgREST versions older than 8.0 from the versions menu. - -* Removed the deprecated *Embedding table from another schema* how-to. - -Bug fixes ---------- - -* Return ``204 No Content`` without ``Content-Type`` for ``PUT`` (`#2058 `_) - -* Clarify error for failed schema cache load. (`#2107 `_) - - - From ``Database connection lost. Retrying the connection`` to ``Could not query the database for the schema cache. Retrying.`` - -* Fix silently ignoring filter on a non-existent embedded resource (`#1771 `_) - -* Remove functions, which are not callable due to unnamed arguments, from schema cache and OpenAPI output. (`#2152 `_) - -* Fix accessing JSON array fields with ``->`` and ``->>`` in ``?select=`` and ``?order=``. (`#2145 `_) - -Breaking changes ----------------- - -* Return ``204 No Content`` without ``Content-Type`` for RPCs returning ``VOID`` (`#2001 `_) - - - Previously, those RPCs would return ``null`` as a body with ``Content-Type: application/json``. - -Thanks ------- - -Big thanks from the `PostgREST team `_ to our sponsors! - -.. container:: image-container - - .. image:: ../_static/cybertec-new.png - :target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest - :width: 13em - - .. image:: ../_static/2ndquadrant.png - :target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo - :width: 13em - - .. image:: ../_static/retool.png - :target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest - :width: 13em - - .. image:: ../_static/gnuhost.png - :target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest - :width: 13em - - .. image:: ../_static/supabase.png - :target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage - :width: 13em - - .. image:: ../_static/oblivious.jpg - :target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest - :width: 13em - -* Evans Fernandes -* `Jan Sommer `_ -* `Franz Gusenbauer `_ -* `Daniel Babiak `_ -* Tsingson Qin -* Michel Pelletier -* Jay Hannah -* Robert Stolarz -* Nicholas DiBiase -* Christopher Reid -* Nathan Bouscal -* Daniel Rafaj -* David Fenko -* Remo Rechkemmer -* Severin Ibarluzea -* Tom Saleeba -* Pawel Tyll - -If you like to join them please consider `supporting PostgREST development `_. diff --git a/docs/releases/v10.0.0.rst b/docs/releases/v10.0.0.rst new file mode 100644 index 000000000..4d2a6aea8 --- /dev/null +++ b/docs/releases/v10.0.0.rst @@ -0,0 +1,337 @@ + +PostgREST 10.0.0 +================ + +Features +-------- + +API +~~~ + +XML/SOAP support for RPC +^^^^^^^^^^^^^^^^^^^^^^^^ + +RPC now understands the ``text/xml`` media type, allowing SQL functions to send XML output(``Accept: text/xml``) and receive XML input(``Content-Type: text/xml``). This makes SOAP endpoints possible, check the :ref:`create_soap_endpoint` how-to for more details. + +GeoJSON support +^^^^^^^^^^^^^^^ + +GeoJSON is supported across the board(reads, writes, RPC) with the ``Accept: application/geo+json`` header, this depends on PostGIS from the versions 3.0.0 and up. The :ref:`working with PostGIS section ` has an example to get you started. + +One-to-one relationships +^^^^^^^^^^^^^^^^^^^^^^^^ + +A :ref:`one-to-one relationship ` is now detected when a table's foreign key is also its primary key or when the foreign key has a ``UNIQUE`` constraint. + +Customizable Relationships for Resource Embedding +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Using :ref:`computed_relationships`, you can add custom relationships or override automatically detected ones. This makes :ref:`resource_embedding` possible on Foreign Data Wrappers and complex SQL views. + +EXPLAIN Execution Plan +^^^^^^^^^^^^^^^^^^^^^^ + +The :ref:`EXPLAIN execution plan of a request ` is now obtainable with the ``Accept: application/vnd.pgrst.plan`` header. The result can be in ``text`` or ``json`` formats and is compatible with EXPLAIN vizualizers like `explain.depesz.com `_ or `explain.dalibo.com `_. + +POSIX Regular Expressions +^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can now use two :ref:`pattern matching ` operators for `POSIX regular expressions `_: ``match`` and ``imatch``, equivalent in PostgreSQL to ``~`` and ``~*`` respectively. + +Access composite type fields and array elements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:ref:`Accessing fields of a Composite type or elements of an Array type ` is now possible with the arrow operators(``->``, ``->>``) in the same way you would access a JSON type fields. + +Authorize button for SwaggerUI +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can now activate the "Authorize" button in SwaggerUI by enabling the :ref:`openapi-security-active` configuration. Add your JWT token prepending :code:`Bearer` to it and you'll be able to request protected resources. + +Improved error messages +^^^^^^^^^^^^^^^^^^^^^^^ + +To increase consistency, all the errors messages are now normalized. The ``hint``, ``details``, ``code`` and ``message`` fields will always be present in the body, each one defaulting to a +``null`` value. In the same way, the :ref:`errors that were raised ` with ``SQLSTATE`` now include the ``message`` and ``code`` in the body. + +To further clarify the source of an error, we now add a ``PGRST`` prefix to the error code of all the errors that are PostgREST-specific and don't come from the database. These errors have unique codes that identifies them and are documented in the :ref:`pgrst_errors` section. + +Administration +~~~~~~~~~~~~~~ + +Health checks +^^^^^^^^^^^^^ + +Admins can now benefit from two :ref:`health check endpoints ` exposed in a different port than the main app. When activated, the ``live`` and ``ready`` endpoints are available to verify if PostgREST is alive and running or if the database connection and the :ref:`schema cache ` are ready for querying. + +Logging users +^^^^^^^^^^^^^ + +You can now see the :ref:`request database user in the logs `. + +Run without configuration +^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is now possible to execute PostgREST without specifying any configuration variable. The three that were mandatory on the previous versions, are no longer so. + + - If :ref:`db-uri` is not set, PostgREST will use the `libpq environment variables `_ for the database connection. + - If :ref:`db-schemas` is not set, it will use the database ``public`` schema. + - If :ref:`db-anon-role` is not set, it will not allow anonymous requests. + +Documentation improvements +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Added a :doc:`/how-tos/working-with-postgresql-data-types` how-to, which contains explanations and examples on how to work with different PostgreSQL data types such as timestamps, ranges or PostGIS types, among others. + +* Added in-database and environment variable settings for each :ref:`configuration variable `. + +* Added the :ref:`file_descriptors` subsection. + +* Added a reference page for :doc:`Error documentation `. + +* Moved the :ref:`error_source` and the :ref:`status_codes` sections to the :doc:`errors reference page `. + +* Moved the *Casting type to custom JSON* how-to to the :ref:`casting_range_to_json` subsection. + +* Removed direct links for PostgREST versions older than 8.0 from the versions menu. + +* Removed the deprecated *Embedding table from another schema* how-to. + +* Restructured the :ref:`resource_embedding` section: + + - Added a :ref:`one-to-many` and :ref:`many-to-one` subsections. + + - Renamed the *Embedding through join tables* subsection to :ref:`many-to-many`. + +Bug fixes +--------- + +* Return ``204 No Content`` without ``Content-Type`` for ``PUT`` (`#2058 `_) + +* Clarify error for failed schema cache load. (`#2107 `_) + + - From ``Database connection lost. Retrying the connection`` to ``Could not query the database for the schema cache. Retrying.`` + +* Fix silently ignoring filter on a non-existent embedded resource (`#1771 `_) + +* Remove functions, which are not callable due to unnamed arguments, from schema cache and OpenAPI output. (`#2152 `_) + +* Fix accessing JSON array fields with ``->`` and ``->>`` in ``?select=`` and ``?order=``. (`#2145 `_) + +* Ignore ``max-rows`` on ``POST``, ``PATCH``, ``PUT`` and ``DELETE`` (`#2155 `_) + +* Fix inferring a foreign key column as a primary key column on views (`#2254 `_) + +* Restrict generated many-to-many relationships (`#2070 `_) + + - Only adds many-to-many relationships when a table has foreign keys to two other tables and these foreign key columns are part of the table's primary key columns. + +* Allow casting to types with underscores and numbers (e.g. ``select=oid_array::_int4``) (`#2278 `_) + +* Prevent views from breaking one-to-many/many-to-one embeds when using column or foreign key as target (`#2277 `_, `#2238 `_, `#1643 `_) + + - When using a column or foreign key as target for embedding (``/tbl?select=*,col-or-fk(*)``), only tables are now detected and views are not. + + - You can still use a column or an inferred foreign key on a view to embed a table (``/view?select=*,col-or-fk(*)``) + +* Increase the ``db-pool-timeout`` to 1 hour to prevent frequent high connection latency (`#2317 `_) + +* The search path now correctly identifies schemas with uppercase and special characters in their names (regression) (`#2341 `_) + +* "404 Not Found" on nested routes and "405 Method Not Allowed" errors no longer start an empty database transaction (`#2364 `_) + +* Fix inaccurate result count when an inner embed was selected after a normal embed in the query string (`#2342 `_) + +* ``OPTIONS`` requests no longer start an empty database transaction (`#2376 `_) + +* Allow using columns with dollar sign ($) without double quoting in filters and ``select`` (`#2395 `_) + +* Fix loop crash error on startup in PostgreSQL 15 beta 3. ``Log: "UNION types \"char\" and text cannot be matched."`` (`#2410 `_) + +* Fix race conditions managing database connection helper (`#2397 `_) + +* Allow ``limit=0`` in the request query to return an empty array (`#2269 `_) + +Breaking changes +---------------- + +* Return ``204 No Content`` without ``Content-Type`` for RPCs returning ``VOID`` (`#2001 `_) + + - Previously, those RPCs would return ``null`` as a body with ``Content-Type: application/json``. + +* ``limit/offset`` now limits the affected rows on ``UPDATE``/``DELETE`` (`#2156 `_) + + - Previously, ``limit``/``offset`` only limited the returned rows but not the actual updated rows + +* ``max-rows`` is no longer applied on ``POST``, ``PATCH``, ``PUT`` and ``DELETE`` returned rows (`#2155 `_) + + - This was misleading because the affected rows were not really affected by ``max-rows``, only the returned rows were limited + +* Restrict generated many-to-many relationships (`#2070 `_) + + - A primary key that contains the foreign key columns is now needed for generating many-to-many relationships. + +* Views now are not detected when embedding using the column or foreign key as target (``/view?select=*,column(*)``) (`#2277 `_) + + - This embedding form was easily made ambiguous whenever a new view was added. + + - For migrating, clients must be updated to the embedding form of ``/view?select=*,other_view!column(*)``. + +* Using ``Prefer: return=representation`` no longer returns a ``Location`` header (`#2312 `_) + +Migration Guide +~~~~~~~~~~~~~~~ + +Many-to-may relationships +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The way PostgREST infers many-to-many relationships is now restricted. Before this change, a table could work as an intermediate join between two tables just by having foreign keys referencing each one of them. Consider the following: + +.. code-block:: postgresql + + CREATE TABLE users ( + id INT PRIMARY KEY, + name TEXT + ); + + CREATE TABLE permissions ( + id INT PRIMARY KEY, + name TEXT + ); + + CREATE TABLE permission_user ( + id INT PRIMARY KEY, + user_id INT REFERENCES users(id), + permission_id INT REFERENCES permissions(id) + ); + +Before, PostgREST could infer a relationship between ``users`` and ``permissions`` through ``permission_user``. + +.. tabs:: + + .. code-tab:: http + + GET /users?select=permissions(*) HTTP/1.1 + + .. code-tab:: bash Curl + + curl "http://localhost:3000/users?select=permissions(*)" + +But now this is not allowed. In order for it to work, the intermediate table must also have the foreign keys included in its primary key. So, in this case we need to do the following: + +.. code-block:: postgresql + + -- This table has a pk defined already so we drop it first + alter table permission_user + drop constraint permission_user_pkey; + + -- Then we add all the foreign keys to the primary key + alter table permission_user + add primary key (id, user_id, permission_id); + +With this, PostgREST 10 will infer successfully a relationship between ``users`` and ``permissions``. + +If you want an alternative to the previous method or need a more customized relationship, you could use :ref:`computed_relationships` to get a similar result. + +Embedding views +^^^^^^^^^^^^^^^ + +Using column names or foreign key constraint names as :ref:`embedding targets ` will not detect views anymore. Consider this as an example: + +.. code-block:: postgresql + + CREATE TABLE users ( + id INT PRIMARY KEY, + name TEXT, + is_active BOOL + ); + + CREATE TABLE messages ( + id INT PRIMARY KEY, + body TEXT, + user_id INT REFERENCES users(id) + ); + + CREATE VIEW active_users AS + SELECT * + FROM users + WHERE is_active; + +Previously, the following request returned a ``300 Multiple Choices`` error, because the ``active_users`` view was also detected: + +.. tabs:: + + .. code-tab:: http + + GET /messages?select=body,user_id(name) HTTP/1.1 + + .. code-tab:: bash Curl + + curl "http://localhost:3000/messages?select=body,user_id(name)" + +But in this version, this will not fail and will embed the table ``users`` instead. You need to use the view name as target in order to embed it, like this: + +.. tabs:: + + .. code-tab:: http + + GET /messages?select=body,active_users(name) HTTP/1.1 + + .. code-tab:: bash Curl + + curl "http://localhost:3000messages?select=body,active_users(name)" + +For other cases, adding a column or foreign key as :ref:`hint ` may be needed. + +You could also use :ref:`computed_relationships` to get a similar result or if you want a more customized relationship. + +Thanks +------ + +Big thanks from the `PostgREST team `_ to our sponsors! + +.. container:: image-container + + .. image:: ../_static/cybertec-new.png + :target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest + :width: 13em + + .. image:: ../_static/2ndquadrant.png + :target: https://www.2ndquadrant.com/en/?utm_campaign=External%20Websites&utm_source=PostgREST&utm_medium=Logo + :width: 13em + + .. image:: ../_static/retool.png + :target: https://retool.com/?utm_source=sponsor&utm_campaign=postgrest + :width: 13em + + .. image:: ../_static/gnuhost.png + :target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest + :width: 13em + + .. image:: ../_static/supabase.png + :target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage + :width: 13em + + .. image:: ../_static/oblivious.jpg + :target: https://oblivious.ai/?utm_source=sponsor&utm_campaign=postgrest + :width: 13em + +* Evans Fernandes +* `Jan Sommer `_ +* `Franz Gusenbauer `_ +* `Daniel Babiak `_ +* Tsingson Qin +* Michel Pelletier +* Jay Hannah +* Robert Stolarz +* Nicholas DiBiase +* Christopher Reid +* Nathan Bouscal +* Daniel Rafaj +* David Fenko +* Remo Rechkemmer +* Severin Ibarluzea +* Tom Saleeba +* Pawel Tyll + +If you like to join them please consider `supporting PostgREST development `_. diff --git a/postgrest.dict b/postgrest.dict index d1430e1f6..2105b5839 100644 --- a/postgrest.dict +++ b/postgrest.dict @@ -43,6 +43,7 @@ filename FreeBSD fts GC +GeoJSON GHC Github Google @@ -118,6 +119,7 @@ phraseto plainto plfts poolers +POSIX PostGIS PostgreSQL PostgreSQL's @@ -165,6 +167,7 @@ Stolarz subselect SuperAgent SvelteKit +SwaggerUI syslog systemd Tcl @@ -200,3 +203,5 @@ Websockets webuser wfts ZeroMQ +Customizable +customizable