Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All changes that a user can see are in this file. The version is 0.M.patch, wher

## Unreleased

### Added

- `set_config` (spec/06 section 6.13), which is `SET` as a function. It takes the same values and gives the same errors as `SET`, and gives the new value as `SHOW` does. A null value resets the parameter. The change counts for the rest of the query, so a `set_config` of `TimeZone` changes the output of the values after it in the same row. An error or a rollback undoes the change, and a local change ends with its transaction.

### Fixed

- After the first query of a transaction, `SET TRANSACTION ISOLATION LEVEL` with a new level, `SET TRANSACTION [NOT] DEFERRABLE` and `SET TRANSACTION READ WRITE` in a read-only transaction now give `25001`, as in PostgreSQL. A query takes the snapshot of the transaction when it is analyzed, in a `Query` or a `Parse` message.

## 0.0.18 (2026-10-08)

The eighteenth patch release on the 0.0 line. `SELECT` now has subqueries in expressions, the 12 OID alias types such as `regclass` and `regtype`, and the catalog visibility and comment functions. No crate is published.
Expand Down
13 changes: 9 additions & 4 deletions crates/rupg-exec/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::rc::Rc;

use rupg_analyze::{Env, Params, analyze};
use rupg_common::Result;
use rupg_sql::nodes::Node;
Expand All @@ -9,7 +11,7 @@ use crate::prepare;

/// A session at 2000-01-11 00:00 UTC with the default settings.
struct TestSession {
zone: FixedZone,
zone: Rc<FixedZone>,
}

impl rupg_func::Session for TestSession {
Expand All @@ -19,8 +21,8 @@ impl rupg_func::Session for TestSession {
fn interval_style(&self) -> IntervalStyle {
IntervalStyle::Postgres
}
fn zone(&self) -> Result<&dyn TimeZone> {
Ok(&self.zone)
fn zone(&self) -> Result<Rc<dyn TimeZone>> {
Ok(Rc::clone(&self.zone) as Rc<dyn TimeZone>)
}
fn extra_float_digits(&self) -> i32 {
1
Expand Down Expand Up @@ -61,6 +63,9 @@ impl rupg_func::Session for TestSession {
fn setting(&self, _: &str) -> Option<String> {
None
}
fn set_setting(&self, _: &str, _: Option<&str>, _: bool) -> Result<String> {
Err(rupg_common::Error::internal("the test session has no settings to change"))
}
}

impl Env for TestSession {
Expand All @@ -87,7 +92,7 @@ impl Env for TestSession {

/// The text of each column of each row of a query, `NULL` for a null value, or the error.
fn run(sql: &str) -> Result<Vec<Vec<String>>> {
let session = TestSession { zone: FixedZone::utc() };
let session = TestSession { zone: Rc::new(FixedZone::utc()) };
let (stmts, _) = rupg_sql::parse(sql).map_err(rupg_common::Error::from)?;
let Some(Some(Node::RawStmt(raw))) = stmts.first() else { panic!("no statement in {sql}") };
let query = analyze(raw.stmt.as_ref().expect("a statement"), &session, &Params::default())?;
Expand Down
4 changes: 2 additions & 2 deletions crates/rupg-func/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub fn output(ty: u32, value: &Value, session: &dyn Session, out: &mut Vec<u8>)
types::timestamp_out(*v, session.date_format(), out).map_err(type_error)?;
}
(_, Value::TimestampTz(v)) => {
types::timestamptz_out(*v, session.date_format(), session.zone()?, out)
types::timestamptz_out(*v, session.date_format(), &*session.zone()?, out)
.map_err(type_error)?;
}
(_, Value::Interval(v)) => types::interval_out(v, session.interval_style(), out),
Expand Down Expand Up @@ -249,7 +249,7 @@ pub fn input(ty: u32, text: &str, typmod: i32, session: &dyn Session) -> Result<
}
oid::DATE | oid::TIME | oid::TIMETZ | oid::TIMESTAMP | oid::TIMESTAMPTZ => {
let zone = session.zone()?;
let cx = datetime_input(session, zone);
let cx = datetime_input(session, &*zone);
match ty {
oid::DATE => Value::Date(types::date_in(text, &cx).map_err(type_error)?),
oid::TIME => Value::Time(types::time_in(text, typmod, &cx).map_err(type_error)?),
Expand Down
9 changes: 8 additions & 1 deletion crates/rupg-func/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod reg;
mod text;

use std::collections::BTreeMap;
use std::rc::Rc;
use std::sync::OnceLock;

use rupg_common::{Error, Result, SqlState};
Expand All @@ -39,7 +40,7 @@ pub trait Session {
/// # Errors
///
/// `0A000` for a zone whose rules the engine does not have yet.
fn zone(&self) -> Result<&dyn TimeZone>;
fn zone(&self) -> Result<Rc<dyn TimeZone>>;
/// The `extra_float_digits` setting.
fn extra_float_digits(&self) -> i32;
/// The `bytea_output` setting.
Expand All @@ -66,6 +67,12 @@ pub trait Session {
fn version(&self) -> String;
/// The value of a setting as `SHOW` gives it, or `None` for a name that is not a setting.
fn setting(&self, name: &str) -> Option<String>;
/// `set_config_option` for `set_config`: sets a parameter for the session, or for the transaction when `local` is true, and gives the new value as `SHOW` gives it. `value` is `None` for a reset.
///
/// # Errors
///
/// The errors of `SET` for the name and the value.
fn set_setting(&self, name: &str, value: Option<&str>, local: bool) -> Result<String>;
}

/// A call of a function.
Expand Down
22 changes: 22 additions & 0 deletions crates/rupg-func/src/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,27 @@ fn current_setting(call: &Call<'_>, args: &[Value]) -> Result<Value> {
))
}

/// `set_config_by_name`: `set_config`, which is `SET` as a function. A null value resets the parameter, and a null `is_local` is false.
fn set_config(call: &Call<'_>, args: &[Value]) -> Result<Value> {
let name = match args.first() {
Some(Value::Null) => {
return Err(Error::new(
SqlState::NULL_VALUE_NOT_ALLOWED,
"SET requires parameter name",
));
}
Some(name) => name.as_str().ok_or_else(bad_value)?,
None => return Err(bad_value()),
};
let value = match args.get(1) {
Some(Value::Null) => None,
Some(value) => Some(value.as_str().ok_or_else(bad_value)?),
None => return Err(bad_value()),
};
let local = args.get(2).and_then(Value::as_bool).unwrap_or(false);
Ok(Value::Text(call.session.set_setting(name, value, local)?))
}

/// The kernel of a function of this module by its `prosrc`.
pub(crate) fn by_src(src: &str) -> Option<Kernel> {
Some(match src {
Expand All @@ -162,6 +183,7 @@ pub(crate) fn by_src(src: &str) -> Option<Kernel> {
"pg_client_encoding" => pg_client_encoding,
"format_type" => format_type,
"show_config_by_name" | "show_config_by_name_missing_ok" => current_setting,
"set_config_by_name" => set_config,
_ => return None,
})
}
Expand Down
13 changes: 9 additions & 4 deletions crates/rupg-func/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::rc::Rc;

use rupg_common::{Result, SqlState};
use rupg_pgcatalog::builtin;
use rupg_types::{
Expand All @@ -8,12 +10,12 @@ use rupg_types::{
use crate::{Call, Session, kernel, output};

struct TestSession {
zone: FixedZone,
zone: Rc<FixedZone>,
}

impl TestSession {
fn new() -> TestSession {
TestSession { zone: FixedZone::utc() }
TestSession { zone: Rc::new(FixedZone::utc()) }
}
}

Expand All @@ -24,8 +26,8 @@ impl Session for TestSession {
fn interval_style(&self) -> IntervalStyle {
IntervalStyle::Postgres
}
fn zone(&self) -> Result<&dyn TimeZone> {
Ok(&self.zone)
fn zone(&self) -> Result<Rc<dyn TimeZone>> {
Ok(Rc::clone(&self.zone) as Rc<dyn TimeZone>)
}
fn extra_float_digits(&self) -> i32 {
1
Expand Down Expand Up @@ -66,6 +68,9 @@ impl Session for TestSession {
fn setting(&self, name: &str) -> Option<String> {
(name == "datestyle").then(|| "ISO, MDY".to_string())
}
fn set_setting(&self, _: &str, _: Option<&str>, _: bool) -> Result<String> {
Err(rupg_common::Error::internal("the test session has no settings to change"))
}
}

/// A call of the function in `pg_catalog` with this name and these argument types.
Expand Down
Loading
Loading