Skip to content

Commit

Permalink
sql: add stub for pg_timezone_abbrevs
Browse files Browse the repository at this point in the history
These zone abbreviations aren't actually *used* by `AT TIME ZONE` and
friends, but embedded them in the binary like this is the first step,
and savvy users can query the `pg_timezone_abbrevs` table directly to
assist with hand-rolled timezone abbreviation logic.
  • Loading branch information
benesch authored and maddyblue committed Nov 4, 2023
1 parent b6ac8fc commit 980e9c1
Show file tree
Hide file tree
Showing 13 changed files with 1,117 additions and 0 deletions.
31 changes: 31 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ members = [
"src/pgrepr",
"src/pgrepr-consts",
"src/pgtest",
"src/pgtz",
"src/pgwire",
"src/pgwire-common",
"src/pid-file",
Expand Down
1 change: 1 addition & 0 deletions bin/lint
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ copyright_files=$(grep -vE \
-e '^ci/test/lint-deps/' \
-e 'test/chbench/chbench' \
-e 'src/pid-file/libbsd' \
-e 'src/pgtz/tznames/.*' \
-e 'test/sqllogictest/postgres/testdata/.*\.data' \
-e 'test/pgtest/.*\.pt' \
-e 'test/pgtest-mz/.*\.pt' \
Expand Down
1 change: 1 addition & 0 deletions doc/user/content/sql/system-catalog/pg_catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ system catalog](https://www.postgresql.org/docs/current/catalogs.html):
* [`pg_shdescription`](https://www.postgresql.org/docs/current/catalog-pg-shdescription.html)
* [`pg_tables`](https://www.postgresql.org/docs/current/view-pg-tables.html)
* [`pg_tablespace`](https://www.postgresql.org/docs/current/catalog-pg-tablespace.html)
* [`pg_timezone_abbrevs`](https://www.postgresql.org/docs/current/view-pg-timezone-abbrevs.html)
* [`pg_trigger`](https://www.postgresql.org/docs/current/catalog-pg-trigger.html)
* [`pg_type`](https://www.postgresql.org/docs/current/catalog-pg-type.html)
* [`pg_views`](https://www.postgresql.org/docs/current/view-pg-views.html)
Expand Down
2 changes: 2 additions & 0 deletions src/catalog/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ anyhow = "1.0.66"
async-trait = "0.1.68"
bytes = { version = "1.3.0", features = ["serde"] }
chrono = { version = "0.4.23", default-features = false, features = ["std"] }
const_format = "0.2.32"
derivative = "2.2.0"
differential-dataflow = "0.12.0"
futures = "0.3.25"
Expand All @@ -26,6 +27,7 @@ mz-ore = { path = "../ore", features = ["chrono", "async", "tracing_"] }
mz-persist-client = { path = "../persist-client" }
mz-persist-types = { path = "../persist-types" }
mz-pgrepr = { path = "../pgrepr" }
mz-pgtz = { path = "../pgtz" }
mz-proto = { path = "../proto" }
mz-repr = { path = "../repr", features = ["tracing_"] }
mz-sql = { path = "../sql" }
Expand Down
12 changes: 12 additions & 0 deletions src/catalog/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

use std::hash::Hash;

use const_format::concatcp;
use mz_compute_client::logging::{ComputeLog, DifferentialLog, LogVariant, TimelyLog};
use mz_pgrepr::oid;
use mz_repr::adt::mz_acl_item::{AclMode, MzAclItem};
Expand Down Expand Up @@ -3334,6 +3335,16 @@ pub const PG_SHDESCRIPTION: BuiltinView = BuiltinView {
sensitivity: DataSensitivity::Public,
};

pub const PG_TIMEZONE_ABBREVS: BuiltinView = BuiltinView {
name: "pg_timezone_abbrevs",
schema: PG_CATALOG_SCHEMA,
sql: concatcp!(
"CREATE VIEW pg_catalog.pg_timezone_abbrevs (abbrev, utc_offset, is_dst) AS ",
mz_pgtz::TIMEZONE_ABBREV_SQL,
),
sensitivity: DataSensitivity::Public,
};

pub const MZ_PEEK_DURATIONS_HISTOGRAM_PER_WORKER: BuiltinView = BuiltinView {
name: "mz_peek_durations_histogram_per_worker",
schema: MZ_INTERNAL_SCHEMA,
Expand Down Expand Up @@ -5671,6 +5682,7 @@ pub static BUILTINS_STATIC: Lazy<Vec<Builtin<NameReference>>> = Lazy::new(|| {
Builtin::View(&PG_LANGUAGE),
Builtin::View(&PG_SHDESCRIPTION),
Builtin::View(&PG_INDEXES),
Builtin::View(&PG_TIMEZONE_ABBREVS),
Builtin::View(&INFORMATION_SCHEMA_APPLICABLE_ROLES),
Builtin::View(&INFORMATION_SCHEMA_COLUMNS),
Builtin::View(&INFORMATION_SCHEMA_ENABLED_ROLES),
Expand Down
17 changes: 17 additions & 0 deletions src/pgtz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "mz-pgtz"
description = "PostgreSQL-compatible timezone handling."
version = "0.0.0"
edition.workspace = true
rust-version.workspace = true
publish = false

[dependencies]
workspace-hack = { version = "0.0.0", path = "../workspace-hack", optional = true }

[build-dependencies]
anyhow = "1.0.66"
mz-ore = { path = "../ore" }

[package.metadata.cargo-udeps.ignore]
normal = ["workspace-hack"]
155 changes: 155 additions & 0 deletions src/pgtz/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file at the
// root of this repository, or online at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// BEGIN LINT CONFIG
// DO NOT EDIT. Automatically generated by bin/gen-lints.
// Have complaints about the noise? See the note in misc/python/materialize/cli/gen-lints.py first.
#![allow(unknown_lints)]
#![allow(clippy::style)]
#![allow(clippy::complexity)]
#![allow(clippy::large_enum_variant)]
#![allow(clippy::mutable_key_type)]
#![allow(clippy::stable_sort_primitive)]
#![allow(clippy::map_entry)]
#![allow(clippy::box_default)]
#![allow(clippy::drain_collect)]
#![warn(clippy::bool_comparison)]
#![warn(clippy::clone_on_ref_ptr)]
#![warn(clippy::no_effect)]
#![warn(clippy::unnecessary_unwrap)]
#![warn(clippy::dbg_macro)]
#![warn(clippy::todo)]
#![warn(clippy::wildcard_dependencies)]
#![warn(clippy::zero_prefixed_literal)]
#![warn(clippy::borrowed_box)]
#![warn(clippy::deref_addrof)]
#![warn(clippy::double_must_use)]
#![warn(clippy::double_parens)]
#![warn(clippy::extra_unused_lifetimes)]
#![warn(clippy::needless_borrow)]
#![warn(clippy::needless_question_mark)]
#![warn(clippy::needless_return)]
#![warn(clippy::redundant_pattern)]
#![warn(clippy::redundant_slicing)]
#![warn(clippy::redundant_static_lifetimes)]
#![warn(clippy::single_component_path_imports)]
#![warn(clippy::unnecessary_cast)]
#![warn(clippy::useless_asref)]
#![warn(clippy::useless_conversion)]
#![warn(clippy::builtin_type_shadow)]
#![warn(clippy::duplicate_underscore_argument)]
#![warn(clippy::double_neg)]
#![warn(clippy::unnecessary_mut_passed)]
#![warn(clippy::wildcard_in_or_patterns)]
#![warn(clippy::crosspointer_transmute)]
#![warn(clippy::excessive_precision)]
#![warn(clippy::overflow_check_conditional)]
#![warn(clippy::as_conversions)]
#![warn(clippy::match_overlapping_arm)]
#![warn(clippy::zero_divided_by_zero)]
#![warn(clippy::must_use_unit)]
#![warn(clippy::suspicious_assignment_formatting)]
#![warn(clippy::suspicious_else_formatting)]
#![warn(clippy::suspicious_unary_op_formatting)]
#![warn(clippy::mut_mutex_lock)]
#![warn(clippy::print_literal)]
#![warn(clippy::same_item_push)]
#![warn(clippy::useless_format)]
#![warn(clippy::write_literal)]
#![warn(clippy::redundant_closure)]
#![warn(clippy::redundant_closure_call)]
#![warn(clippy::unnecessary_lazy_evaluations)]
#![warn(clippy::partialeq_ne_impl)]
#![warn(clippy::redundant_field_names)]
#![warn(clippy::transmutes_expressible_as_ptr_casts)]
#![warn(clippy::unused_async)]
#![warn(clippy::disallowed_methods)]
#![warn(clippy::disallowed_macros)]
#![warn(clippy::disallowed_types)]
#![warn(clippy::from_over_into)]
// END LINT CONFIG

use std::path::PathBuf;
use std::{env, fs};

use anyhow::{bail, Context, Result};
use mz_ore::codegen::CodegenBuf;

const DEFAULT_TZNAMES: &str = "tznames/Default";

fn main() -> Result<()> {
let out_dir = PathBuf::from(env::var_os("OUT_DIR").context("Cannot read OUT_DIR env var")?);

let mut sql_buf = CodegenBuf::new();
let mut rust_buf = CodegenBuf::new();

sql_buf.writeln("VALUES");

let tznames = fs::read_to_string(DEFAULT_TZNAMES)?;
let mut emitted_abbrev = false;
for (i, line) in tznames.lines().enumerate() {
let pieces = line.split_ascii_whitespace().collect::<Vec<_>>();

if let Some(p) = pieces.first() {
if p.starts_with('#') {
// Comment line.
continue;
}
} else if pieces.len() == 0 {
// Empty line.
continue;
} else if pieces.len() < 2 {
bail!("line {}: did not find at least two fields", i + 1);
}

let abbrev = pieces[0];
let utc_offset_secs = match pieces[1].parse::<i32>() {
Ok(utc_offset_secs) => utc_offset_secs,
Err(_) => {
// Link to timezone rather than fixed offset from UTC. These
// hard to handle as the offset from UTC changes depending on
// the current date/time. Skip for now.
continue;
}
};
let is_dst = pieces.get(2) == Some(&"D");

rust_buf.write_block(
format!("pub const {abbrev}: TimezoneAbbrev = TimezoneAbbrev"),
|rust_buf| {
rust_buf.writeln(format!("abbrev: \"{abbrev}\","));
rust_buf.writeln(format!("utc_offset_secs: {utc_offset_secs},"));
rust_buf.writeln(format!("is_dst: {is_dst}"));
},
);
rust_buf.writeln(";");

if emitted_abbrev {
sql_buf.writeln(",");
}
sql_buf.write(format!(
"('{abbrev}', interval '{utc_offset_secs} seconds', {is_dst})"
));

emitted_abbrev = true;
}

sql_buf.end_line();

fs::write(out_dir.join("gen.sql"), sql_buf.into_string())?;
fs::write(out_dir.join("gen.rs"), rust_buf.into_string())?;

Ok(())
}
92 changes: 92 additions & 0 deletions src/pgtz/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright Materialize, Inc. and contributors. All rights reserved.
//
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0.

// BEGIN LINT CONFIG
// DO NOT EDIT. Automatically generated by bin/gen-lints.
// Have complaints about the noise? See the note in misc/python/materialize/cli/gen-lints.py first.
#![allow(unknown_lints)]
#![allow(clippy::style)]
#![allow(clippy::complexity)]
#![allow(clippy::large_enum_variant)]
#![allow(clippy::mutable_key_type)]
#![allow(clippy::stable_sort_primitive)]
#![allow(clippy::map_entry)]
#![allow(clippy::box_default)]
#![allow(clippy::drain_collect)]
#![warn(clippy::bool_comparison)]
#![warn(clippy::clone_on_ref_ptr)]
#![warn(clippy::no_effect)]
#![warn(clippy::unnecessary_unwrap)]
#![warn(clippy::dbg_macro)]
#![warn(clippy::todo)]
#![warn(clippy::wildcard_dependencies)]
#![warn(clippy::zero_prefixed_literal)]
#![warn(clippy::borrowed_box)]
#![warn(clippy::deref_addrof)]
#![warn(clippy::double_must_use)]
#![warn(clippy::double_parens)]
#![warn(clippy::extra_unused_lifetimes)]
#![warn(clippy::needless_borrow)]
#![warn(clippy::needless_question_mark)]
#![warn(clippy::needless_return)]
#![warn(clippy::redundant_pattern)]
#![warn(clippy::redundant_slicing)]
#![warn(clippy::redundant_static_lifetimes)]
#![warn(clippy::single_component_path_imports)]
#![warn(clippy::unnecessary_cast)]
#![warn(clippy::useless_asref)]
#![warn(clippy::useless_conversion)]
#![warn(clippy::builtin_type_shadow)]
#![warn(clippy::duplicate_underscore_argument)]
#![warn(clippy::double_neg)]
#![warn(clippy::unnecessary_mut_passed)]
#![warn(clippy::wildcard_in_or_patterns)]
#![warn(clippy::crosspointer_transmute)]
#![warn(clippy::excessive_precision)]
#![warn(clippy::overflow_check_conditional)]
#![warn(clippy::as_conversions)]
#![warn(clippy::match_overlapping_arm)]
#![warn(clippy::zero_divided_by_zero)]
#![warn(clippy::must_use_unit)]
#![warn(clippy::suspicious_assignment_formatting)]
#![warn(clippy::suspicious_else_formatting)]
#![warn(clippy::suspicious_unary_op_formatting)]
#![warn(clippy::mut_mutex_lock)]
#![warn(clippy::print_literal)]
#![warn(clippy::same_item_push)]
#![warn(clippy::useless_format)]
#![warn(clippy::write_literal)]
#![warn(clippy::redundant_closure)]
#![warn(clippy::redundant_closure_call)]
#![warn(clippy::unnecessary_lazy_evaluations)]
#![warn(clippy::partialeq_ne_impl)]
#![warn(clippy::redundant_field_names)]
#![warn(clippy::transmutes_expressible_as_ptr_casts)]
#![warn(clippy::unused_async)]
#![warn(clippy::disallowed_methods)]
#![warn(clippy::disallowed_macros)]
#![warn(clippy::disallowed_types)]
#![warn(clippy::from_over_into)]
// END LINT CONFIG

/// An abbreviation for a time zone.
pub struct TimezoneAbbrev {
/// The abbreviation for a fixed offset from UTC.
pub abbrev: &'static str,
/// The number of seconds offset from UTC, where positive means east of
/// Greenwich.
pub utc_offset_secs: i32,
/// Whether this is a daylight saving abbreviation.
pub is_dst: bool,
}

include!(concat!(env!("OUT_DIR"), "/gen.rs"));

/// A SQL `VALUES` clause containing all known time zone abbreviations.
pub const TIMEZONE_ABBREV_SQL: &str = include_str!(concat!(env!("OUT_DIR"), "/gen.sql"));
Loading

0 comments on commit 980e9c1

Please sign in to comment.