diff --git a/README.md b/README.md index e5ed2c3..521e075 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,18 @@ Which would yield something like: the rowid option is required. As are the names key and value for the columns. +## Pushdowning + +#### ORDER BY push-down +`etcd_fdw` now also supports order by push-down. If possible, push order by +clause to the remote server so that we get the ordered result set from the +foreign server itself. + +#### LIMIT push-down +`etcd_fdw` now also supports limit offset push-down. Wherever possible, +perform LIMIT operations on the remote server. + + Usage ----- diff --git a/src/lib.rs b/src/lib.rs index a0130e2..9854e2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -use etcd_client::{Client, ConnectOptions, TlsOptions, Identity, Certificate, Error, DeleteOptions, GetOptions, KeyValue, PutOptions}; +use etcd_client::{Client, ConnectOptions, TlsOptions, Identity, Certificate, Error, DeleteOptions, GetOptions, KeyValue, PutOptions, SortTarget, SortOrder}; use std::time::Duration; use pgrx::pg_sys::panic::ErrorReport; use pgrx::PgSqlErrorCode; @@ -86,6 +86,9 @@ pub enum EtcdFdwError { #[error("Invalid option '{0}' with value '{1}'")] InvalidOption(String, String), + #[error("Invalid sort field value '{0}'")] + InvalidSortField(String), + #[error("{0}")] OptionsError(#[from] OptionsError), } @@ -237,7 +240,7 @@ impl ForeignDataWrapper for EtcdFdw { &mut self, _quals: &[Qual], columns: &[Column], - _sorts: &[Sort], + sort: &[Sort], limit: &Option, options: &std::collections::HashMap, ) -> Result<(), EtcdFdwError> { @@ -284,6 +287,30 @@ impl ForeignDataWrapper for EtcdFdw { get_options = get_options.with_serializable(); } + // XXX Support for WHERE clause push-downs is pending + // etcd doesn't have anything like WHERE clause because it + // a NOSQL database. + // But may be we can still support some simple WHERE + // conditions like '<', '>=', 'LIKE', '=' by mapping them + // to key, range_end and prefix options. + + // sort pushdown + if let Some(first_sort) = sort.first() { + let field_name = first_sort.field.to_ascii_uppercase(); + + if let Some(target) = SortTarget::from_str_name(&field_name) { + let order = if first_sort.reversed { + SortOrder::Descend + } else { + SortOrder::Ascend + }; + + get_options = get_options.with_sort(target, order); + } else { + return Err(EtcdFdwError::InvalidSortField(first_sort.field.clone())); + } + } + // preference order : prefix > key_start > default "\0" // samllest possible valid key '\0' let key = prefix.clone()