Canonical TSAPI series DB schema v1.0.0

Single source of truth for the timeseries PostgreSQL schema shared by the
Python (TSAPI_SERIESDB_PG_PY) and C++ (TSAPI_POSTGRES_CPP) libraries. SQL
seeded byte-for-byte from TSAPI_SERIESDB_PG_PY/tsapi_pgdb/sql on 2026-09-02.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
peter 2026-09-02 17:27:26 +01:00
commit eed06559c0
17 changed files with 2080 additions and 0 deletions

4
sql/00_extensions.sql Normal file
View file

@ -0,0 +1,4 @@
-- PostgreSQL extensions
-- Generated: 2025-10-15T12:24:56.026074
CREATE EXTENSION IF NOT EXISTS pg_trgm SCHEMA public;

6
sql/01_schemas.sql Normal file
View file

@ -0,0 +1,6 @@
-- Database schemas
-- Generated: 2025-10-15T12:24:56.026074
CREATE SCHEMA IF NOT EXISTS meta;
CREATE SCHEMA IF NOT EXISTS series;
CREATE SCHEMA IF NOT EXISTS log;

16
sql/02_sequences.sql Normal file
View file

@ -0,0 +1,16 @@
-- Sequences
-- Generated: 2025-10-15T12:24:56.037289
CREATE SEQUENCE meta._table_meta__table_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO CYCLE
;
CREATE SEQUENCE log.event_log_id_seq
AS bigint
START WITH 1
INCREMENT BY 1
NO CYCLE
;

145
sql/03_tables.sql Normal file
View file

@ -0,0 +1,145 @@
-- Meta schema tables
-- Generated: 2025-10-15T12:24:56.295509
CREATE UNLOGGED TABLE log.pending_changes (
id BIGSERIAL PRIMARY KEY,
"timestamp" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
series_name TEXT NOT NULL,
db_schema TEXT NOT NULL,
op_type TEXT NOT NULL,
db_user TEXT DEFAULT CURRENT_USER,
details JSONB
);
CREATE TABLE log.event_log (
id BIGINT NOT NULL DEFAULT nextval('log.event_log_id_seq'),
"timestamp" TIMESTAMPTZ,
series_name TEXT NOT NULL,
db_schema TEXT NOT NULL,
event_type TEXT NOT NULL,
details JSONB,
source TEXT,
db_user TEXT,
PRIMARY KEY (id)
);
CREATE TABLE meta.currencies (
code character varying(3) NOT NULL,
name character varying(100) NOT NULL,
symbol character varying(10),
decimal_places integer DEFAULT 2,
notes text
);
CREATE TABLE meta.exchanges (
id character varying(12) NOT NULL,
name character varying(50),
timezone character varying(35),
currency character varying(3),
default_sec_type character varying(18),
default_session character varying(40),
default_holiday_calendar character varying(40),
default_tick_size double precision,
default_tick_value double precision,
default_extended_hours boolean DEFAULT false,
country character varying(3)
);
CREATE TABLE meta.holidays (
calendar_name character varying(50) NOT NULL,
date date NOT NULL,
description character varying(255),
early_close boolean NOT NULL DEFAULT false,
PRIMARY KEY (calendar_name, date)
);
CREATE TABLE meta.instruments (
symbol character varying(12) NOT NULL,
exchange character varying(12) NOT NULL,
name character varying(50) NOT NULL,
type character varying(10),
tick_size double precision,
tick_value double precision,
expiry character varying(20),
strike double precision,
currency character varying(3),
session character varying(40),
holiday_calendar character varying(50),
sec_id character varying(50),
sec_id_type character varying(20),
description character varying(255),
country character varying(3),
extended_hours boolean,
sector character varying(50),
industry character varying(100),
category character varying(50),
subcategory character varying(50),
asset_class character varying(30),
geography character varying(30),
underlying character varying(30)
);
CREATE TABLE meta.security_types (
code character varying(10) NOT NULL,
name character varying(50) NOT NULL,
description text,
notes text
);
CREATE TABLE meta.schema_version (
version character varying(20) NOT NULL,
installed_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
description text
);
CREATE TABLE meta.series (
table_id integer DEFAULT nextval('meta._table_meta__table_id_seq'::regclass) NOT NULL,
db_schema character varying(70) NOT NULL,
table_name character varying(70) NOT NULL,
type character varying(30),
description character varying(255),
column_schema text DEFAULT ''::text,
symbol character varying(30),
root_symbol character varying(30),
exchange character varying(30),
frequency character varying(30),
session text,
sec_type character varying(30),
sec_id character varying(30),
sec_id_type character varying(30),
create_timestamp timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
num_records bigint,
first_timestamp timestamp without time zone,
last_timestamp timestamp without time zone,
bar_type character varying(30),
feed character varying(30),
currency character varying(30),
expiry character varying(30),
roll_type character varying(30),
feed_type character varying(30),
user_attr text,
user_attr_2 text,
script text,
notes text,
timezone character varying(50),
category character varying(30),
subcategory character varying(30),
json JSONB,
needs_reindex boolean NOT NULL DEFAULT false
);
CREATE TABLE meta.sessions (
name character varying(50) NOT NULL,
definition text NOT NULL,
"json" text,
exchange_id character varying(12) NOT NULL
);
CREATE TABLE meta.timezones (
iana_name character varying(50) NOT NULL,
display_name character varying(100) NOT NULL,
description text,
region character varying(50),
utc_offset_hours real,
notes text
);

46
sql/04_indexes.sql Normal file
View file

@ -0,0 +1,46 @@
-- Indexes and constraints
-- Generated: 2025-10-15T12:24:56.570321
-- Primary key constraints for metadata tables
ALTER TABLE meta.instruments ADD CONSTRAINT instruments_pkey PRIMARY KEY (symbol, exchange);
ALTER TABLE meta.exchanges ADD CONSTRAINT exchanges_pkey PRIMARY KEY (id);
ALTER TABLE meta.sessions ADD CONSTRAINT sessions_pkey PRIMARY KEY (name);
-- Identity constraints for the series registry and reference tables.
--
-- These are small registries, not timeseries data: a btree key here costs
-- almost nothing. This is unrelated to the deliberate decision NOT to put a
-- unique constraint on series timestamps, where BRIN is used precisely
-- because a btree would be prohibitive at millions of rows per series.
--
-- Without these, duplicate registry rows were possible and the add_* methods
-- advertised "already exists" errors that no constraint could raise.
ALTER TABLE meta.series ADD CONSTRAINT series_pkey PRIMARY KEY (table_id);
ALTER TABLE meta.series ADD CONSTRAINT series_schema_table_key UNIQUE (db_schema, table_name);
ALTER TABLE meta.currencies ADD CONSTRAINT currencies_pkey PRIMARY KEY (code);
ALTER TABLE meta.security_types ADD CONSTRAINT security_types_pkey PRIMARY KEY (code);
ALTER TABLE meta.timezones ADD CONSTRAINT timezones_pkey PRIMARY KEY (iana_name);
CREATE INDEX idx_event_log_series_timestamp ON log.event_log USING btree (series_name, "timestamp" DESC);
CREATE INDEX idx_event_log_timestamp ON log.event_log USING btree ("timestamp" DESC);
CREATE INDEX idx_event_log_event_type ON log.event_log USING btree (event_type);
CREATE INDEX idx_series_create_timestamp_desc ON meta.series USING btree (create_timestamp DESC NULLS LAST);
CREATE INDEX idx_series_db_schema ON meta.series USING btree (db_schema) WHERE ((db_schema IS NOT NULL) AND ((db_schema)::text <> ''::text));
CREATE INDEX idx_series_exchange_pattern ON meta.series USING btree (lower((exchange)::text) text_pattern_ops) WHERE ((exchange IS NOT NULL) AND ((exchange)::text <> ''::text));
CREATE INDEX idx_series_frequency_pattern ON meta.series USING btree (lower((frequency)::text) text_pattern_ops) WHERE ((frequency IS NOT NULL) AND ((frequency)::text <> ''::text));
CREATE INDEX idx_series_symbol_exchange ON meta.series USING btree (lower((symbol)::text), lower((exchange)::text)) WHERE ((symbol IS NOT NULL) AND ((symbol)::text <> ''::text) AND (exchange IS NOT NULL) AND ((exchange)::text <> ''::text));
CREATE INDEX idx_series_symbol_pattern ON meta.series USING btree (lower((symbol)::text) text_pattern_ops) WHERE ((symbol IS NOT NULL) AND ((symbol)::text <> ''::text));
CREATE INDEX idx_series_symbol_trgm ON meta.series USING gin (symbol public.gin_trgm_ops) WHERE ((symbol IS NOT NULL) AND ((symbol)::text <> ''::text));
CREATE INDEX idx_series_table_name_trgm ON meta.series USING gin (table_name public.gin_trgm_ops) WHERE ((table_name IS NOT NULL) AND ((table_name)::text <> ''::text));
CREATE INDEX idx_series_type_pattern ON meta.series USING btree (lower((type)::text) text_pattern_ops) WHERE ((type IS NOT NULL) AND ((type)::text <> ''::text));

426
sql/05_procedures.sql Normal file
View file

@ -0,0 +1,426 @@
-- Stored procedures and functions
-- Generated: 2025-10-15T12:24:56.582392
-- Log schema functions
-- Function: log.notify_event_log
-- Writes a breadcrumb to the staging table (UNLOGGED, fast).
-- Self-drains every 20,000th insert to prevent unbounded growth.
CREATE OR REPLACE FUNCTION log.notify_event_log(
p_series_name TEXT,
p_db_schema TEXT,
p_op_type TEXT,
p_details JSONB DEFAULT NULL
) RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO log.pending_changes (series_name, db_schema, op_type, details)
VALUES (p_series_name, p_db_schema, p_op_type, p_details);
-- Self-drain every 20,000th insert
IF (currval('log.pending_changes_id_seq') % 20000 = 0) THEN
PERFORM log.flush_pending_to_event_log();
END IF;
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'notify_event_log failed: %', SQLERRM;
END;
$function$;
-- Function: log.flush_pending_to_event_log
-- Drains the staging table into the permanent event_log.
-- Safe for concurrent use: snapshots max(id) before processing.
-- After moving events, verifies that meta.series timestamps match actual
-- data for any series that had data-modifying events. If a mismatch is
-- found, writes a TIMESTAMP_MISMATCH event to log.event_log for visibility.
-- (Timestamps are kept accurate by the application on every data operation,
-- so this step is a safety net, not a correction mechanism.)
CREATE OR REPLACE FUNCTION log.flush_pending_to_event_log()
RETURNS INT
LANGUAGE plpgsql
AS $function$
DECLARE
batch_id BIGINT;
v_count INT;
_rec RECORD;
_actual_first TIMESTAMP;
_actual_last TIMESTAMP;
_meta_first TIMESTAMP;
_meta_last TIMESTAMP;
BEGIN
-- Serialise flushes. Snapshotting MAX(id) does not make this safe on its
-- own: two callers can both read the same batch_id, both copy those rows
-- to log.event_log, and only then delete them -- duplicating permanent
-- audit events. The lock is transaction-scoped, so it is released when
-- this function's transaction ends, however it ends.
PERFORM pg_advisory_xact_lock(hashtext('tsapi_pgdb.flush_event_log'));
SELECT COALESCE(MAX(id), 0) INTO batch_id FROM log.pending_changes;
IF batch_id = 0 THEN RETURN 0; END IF;
-- 1. Move rows from staging to permanent event_log
INSERT INTO log.event_log ("timestamp", series_name, db_schema, event_type, details, source, db_user)
SELECT "timestamp", series_name, db_schema, op_type, details, 'app', db_user
FROM log.pending_changes
WHERE id <= batch_id
ORDER BY id;
GET DIAGNOSTICS v_count = ROW_COUNT;
-- 2. Verify meta.series timestamps for series with data-modifying events
FOR _rec IN
SELECT DISTINCT series_name, db_schema
FROM log.pending_changes
WHERE id <= batch_id
AND op_type IN (
'CSV_IMPORT', 'JSON_APPEND', 'DATAFRAME_IMPORT',
'DELETE_FROM', 'DELETE_AFTER', 'DELETE_RANGE', 'DELETE_UNTIL',
'DELETE_WEEKENDS', 'DELETE_TIME_RANGE', 'SERIES_TRUNCATED'
)
LOOP
BEGIN
EXECUTE format(
'SELECT MIN(timestamp), MAX(timestamp) FROM %I.%I',
_rec.db_schema, _rec.series_name
) INTO _actual_first, _actual_last;
SELECT first_timestamp, last_timestamp INTO _meta_first, _meta_last
FROM meta.series
WHERE table_name = _rec.series_name AND db_schema = _rec.db_schema;
IF _meta_first IS DISTINCT FROM _actual_first
OR _meta_last IS DISTINCT FROM _actual_last THEN
INSERT INTO log.event_log
("timestamp", series_name, db_schema, event_type, details, source, db_user)
VALUES (
CURRENT_TIMESTAMP,
_rec.series_name,
_rec.db_schema,
'TIMESTAMP_MISMATCH',
jsonb_build_object(
'meta_first', _meta_first,
'meta_last', _meta_last,
'actual_first', _actual_first,
'actual_last', _actual_last
),
'flush_verify',
CURRENT_USER
);
END IF;
EXCEPTION WHEN OTHERS THEN
NULL; -- table may have been dropped in same batch
END;
END LOOP;
-- 3. Clear staging
DELETE FROM log.pending_changes WHERE id <= batch_id;
RETURN v_count;
END;
$function$;
-- Meta schema functions
-- Function: meta.update_series_last_timestamp
-- Lightweight timestamp update for append operations.
-- Uses GREATEST to conditionally update last_timestamp (no table scan).
-- Sets first_timestamp on first insert via COALESCE.
-- p_first_timestamp is optional: when NULL, uses p_last_timestamp for first_timestamp.
-- For single inserts, only p_last_timestamp is needed.
-- For batch inserts, pass min timestamp as p_first_timestamp.
-- Both Python and C++ clients call this after appending data.
CREATE OR REPLACE FUNCTION meta.update_series_last_timestamp(
p_table_name TEXT,
p_db_schema TEXT DEFAULT 'series',
p_last_timestamp TIMESTAMP DEFAULT NULL,
p_first_timestamp TIMESTAMP DEFAULT NULL
) RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE meta.series
SET first_timestamp = COALESCE(first_timestamp, p_first_timestamp, p_last_timestamp),
last_timestamp = GREATEST(last_timestamp, p_last_timestamp)
WHERE table_name = p_table_name AND db_schema = p_db_schema;
END;
$function$;
-- Function: meta.refresh_series_timestamps
-- Full timestamp refresh using MIN/MAX scan.
-- Used after deletes, truncates, or any operation that may change first_timestamp.
-- BRIN indexes make MIN/MAX fast even on large tables.
-- Both Python and C++ clients call this after data deletion.
CREATE OR REPLACE FUNCTION meta.refresh_series_timestamps(
p_table_name TEXT,
p_db_schema TEXT DEFAULT 'series'
) RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
_first_ts TIMESTAMP;
_last_ts TIMESTAMP;
BEGIN
EXECUTE format(
'SELECT MIN(timestamp), MAX(timestamp) FROM %I.%I',
p_db_schema, p_table_name
) INTO _first_ts, _last_ts;
UPDATE meta.series
SET first_timestamp = _first_ts, last_timestamp = _last_ts
WHERE table_name = p_table_name AND db_schema = p_db_schema;
END;
$function$;
-- Function: meta.drop_all_series
CREATE OR REPLACE FUNCTION meta.drop_all_series()
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
_rec record;
BEGIN
-- Set the context to allow drops
PERFORM set_config('application.dropping_series', 'true', true);
-- Loop through all series in series table
FOR _rec IN SELECT "table_name", "db_schema" FROM meta.series
LOOP
-- Drop each table
EXECUTE format('DROP TABLE IF EXISTS %I.%I', _rec."db_schema", _rec."table_name");
END LOOP;
-- Clear the series table
DELETE FROM meta.series;
-- Reset the context
PERFORM set_config('application.dropping_series', 'false', true);
END;
$function$;
-- Function: meta.drop_series
CREATE OR REPLACE FUNCTION meta.drop_series(_table_name text, _schema_name text DEFAULT 'series'::text, _raise_if_missing boolean DEFAULT true)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- Check if the series exists in series table
IF NOT EXISTS (
SELECT 1 FROM meta.series
WHERE "table_name" = _table_name
AND "db_schema" = _schema_name
) THEN
IF _raise_if_missing THEN
RAISE EXCEPTION 'Series %.% does not exist', _schema_name, _table_name;
END IF;
RETURN;
END IF;
-- Set the context to allow drops
PERFORM set_config('application.dropping_series', 'true', true);
-- Drop the table
EXECUTE format('DROP TABLE IF EXISTS %I.%I', _schema_name, _table_name);
-- Remove from series table
DELETE FROM meta.series
WHERE "table_name" = _table_name
AND "db_schema" = _schema_name;
-- Reset the context
PERFORM set_config('application.dropping_series', 'false', true);
END;
$function$;
-- Function: meta.drop_series_if_exists
CREATE OR REPLACE FUNCTION meta.drop_series_if_exists(_table_name text, _schema_name text DEFAULT 'series'::text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- Check if the series exists in series table
IF EXISTS (
SELECT 1 FROM meta.series
WHERE "table_name" = _table_name
AND "db_schema" = _schema_name
) THEN
-- Signal the prevent_direct_drop event trigger that this DROP is
-- coming from a sanctioned procedure. Without it the trigger rejects
-- the statement below, because the table is registered in meta.series.
-- Local to the transaction, so it cannot leak to later statements.
PERFORM set_config('application.dropping_series', 'true', true);
-- Drop the table
EXECUTE format('DROP TABLE IF EXISTS %I.%I', _schema_name, _table_name);
-- Remove from series table
DELETE FROM meta.series
WHERE "table_name" = _table_name
AND "db_schema" = _schema_name;
PERFORM set_config('application.dropping_series', 'false', true);
END IF;
END;
$function$;
-- Function: meta.prevent_direct_drop
CREATE OR REPLACE FUNCTION meta.prevent_direct_drop()
RETURNS event_trigger
LANGUAGE plpgsql
AS $function$
DECLARE
obj record;
BEGIN
-- Get the current context
IF current_setting('application.dropping_series', true) = 'true' THEN
-- Allow the drop if it's coming from our procedures
RETURN;
END IF;
FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
WHERE object_type = 'table'
LOOP
IF EXISTS (
SELECT 1 FROM meta.series
WHERE table_name = obj.object_name
AND db_schema = obj.schema_name
) THEN
RAISE EXCEPTION 'Table %.% is registered in series and can only be dropped using TimeseriesDB.drop_series()',
obj.schema_name, obj.object_name;
END IF;
END LOOP;
END;
$function$;
-- Function: meta.read_series_batch
CREATE OR REPLACE FUNCTION meta.read_series_batch(p_schema_name text, p_table_name text, p_start_ts timestamp without time zone DEFAULT NULL::timestamp without time zone, p_batch_size integer DEFAULT 10000)
RETURNS TABLE(result_json jsonb)
LANGUAGE plpgsql
AS $function$
DECLARE
v_query TEXT;
v_qualified_name TEXT;
BEGIN
-- Build qualified table name (with proper quoting)
v_qualified_name := format('%I.%I', p_schema_name, p_table_name);
-- Build query with proper WHERE clause
IF p_start_ts IS NULL THEN
-- Read from beginning
v_query := format('
SELECT to_jsonb(t.*) AS result_json
FROM (
SELECT *
FROM %s
ORDER BY timestamp
LIMIT %s
) t
', v_qualified_name, p_batch_size);
ELSE
-- Read from specified timestamp (inclusive)
v_query := format('
SELECT to_jsonb(t.*) AS result_json
FROM (
SELECT *
FROM %s
WHERE timestamp >= %L
ORDER BY timestamp
LIMIT %s
) t
', v_qualified_name, p_start_ts, p_batch_size);
END IF;
-- Execute and return
RETURN QUERY EXECUTE v_query;
END;
$function$;
-- Function: meta.read_series_batch_cursor
CREATE OR REPLACE FUNCTION meta.read_series_batch_cursor(p_schema_name text, p_table_name text, p_start_ts timestamp without time zone DEFAULT NULL::timestamp without time zone, p_batch_size integer DEFAULT 10000, p_cursor_name text DEFAULT 'series_cursor'::text)
RETURNS refcursor
LANGUAGE plpgsql
AS $function$
DECLARE
v_query TEXT;
v_qualified_name TEXT;
v_cursor refcursor;
BEGIN
-- Build qualified table name (with proper quoting)
v_qualified_name := format('%I.%I', p_schema_name, p_table_name);
-- Open cursor with name
v_cursor := p_cursor_name;
-- Build query with proper WHERE clause
IF p_start_ts IS NULL THEN
-- Read from beginning
v_query := format('
SELECT *
FROM %s
ORDER BY timestamp
LIMIT %s
', v_qualified_name, p_batch_size);
ELSE
-- Read from specified timestamp (inclusive)
v_query := format('
SELECT *
FROM %s
WHERE timestamp >= %L
ORDER BY timestamp
LIMIT %s
', v_qualified_name, p_start_ts, p_batch_size);
END IF;
-- Open cursor
OPEN v_cursor FOR EXECUTE v_query;
RETURN v_cursor;
END;
$function$;
-- Function: meta.read_series_batch_ohlc
CREATE OR REPLACE FUNCTION meta.read_series_batch_ohlc(p_schema_name text, p_table_name text, p_start_ts timestamp without time zone DEFAULT NULL::timestamp without time zone, p_batch_size integer DEFAULT 10000)
RETURNS TABLE(ts timestamp without time zone, open double precision, high double precision, low double precision, close double precision, volume bigint)
LANGUAGE plpgsql
AS $function$
DECLARE
v_query TEXT;
v_qualified_name TEXT;
BEGIN
-- Build qualified table name (with proper quoting)
v_qualified_name := format('%I.%I', p_schema_name, p_table_name);
-- Build query with proper WHERE clause
IF p_start_ts IS NULL THEN
-- Read from beginning
v_query := format('
SELECT timestamp AS ts, open, high, low, close, volume
FROM %s
ORDER BY timestamp
LIMIT %s
', v_qualified_name, p_batch_size);
ELSE
-- Read from specified timestamp (inclusive)
v_query := format('
SELECT timestamp AS ts, open, high, low, close, volume
FROM %s
WHERE timestamp >= %L
ORDER BY timestamp
LIMIT %s
', v_qualified_name, p_start_ts, p_batch_size);
END IF;
-- Execute and return
RETURN QUERY EXECUTE v_query;
END;
$function$;

37
sql/06_triggers.sql Normal file
View file

@ -0,0 +1,37 @@
-- Triggers and event triggers
-- Generated: 2025-10-15T12:24:56.582392
-- Event triggers
-- Event trigger: prevent_series_drops
CREATE OR REPLACE FUNCTION meta.prevent_direct_drop()
RETURNS event_trigger
LANGUAGE plpgsql
AS $function$
DECLARE
obj record;
BEGIN
-- Get the current context
IF current_setting('application.dropping_series', true) = 'true' THEN
-- Allow the drop if it's coming from our procedures
RETURN;
END IF;
FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
WHERE object_type = 'table'
LOOP
IF EXISTS (
SELECT 1 FROM meta.series
WHERE table_name = obj.object_name
AND db_schema = obj.schema_name
) THEN
RAISE EXCEPTION 'Table %.% is registered in series and can only be dropped using TimeseriesDB.drop_series()',
obj.schema_name, obj.object_name;
END IF;
END LOOP;
END;
$function$;
CREATE EVENT TRIGGER prevent_series_drops
ON sql_drop
EXECUTE FUNCTION meta.prevent_direct_drop();

769
sql/07_reference_data.sql Normal file
View file

@ -0,0 +1,769 @@
-- Reference data (timezones and currencies)
-- Generated: 2025-10-15T12:24:56.582392
-- Timezones
INSERT INTO meta.timezones (iana_name, display_name, description, region, utc_offset_hours, notes)
VALUES
('Africa/Abidjan', 'Africa/Abidjan', 'IANA timezone: Africa/Abidjan', 'Africa', 0.0, NULL),
('Africa/Accra', 'Africa/Accra', 'IANA timezone: Africa/Accra', 'Africa', 0.0, NULL),
('Africa/Addis_Ababa', 'Africa/Addis Ababa', 'IANA timezone: Africa/Addis_Ababa', 'Africa', 3.0, NULL),
('Africa/Algiers', 'Africa/Algiers', 'IANA timezone: Africa/Algiers', 'Africa', 1.0, NULL),
('Africa/Asmara', 'Africa/Asmara', 'IANA timezone: Africa/Asmara', 'Africa', 3.0, NULL),
('Africa/Asmera', 'Africa/Asmera', 'IANA timezone: Africa/Asmera', 'Africa', 3.0, NULL),
('Africa/Bamako', 'Africa/Bamako', 'IANA timezone: Africa/Bamako', 'Africa', 0.0, NULL),
('Africa/Bangui', 'Africa/Bangui', 'IANA timezone: Africa/Bangui', 'Africa', 1.0, NULL),
('Africa/Banjul', 'Africa/Banjul', 'IANA timezone: Africa/Banjul', 'Africa', 0.0, NULL),
('Africa/Bissau', 'Africa/Bissau', 'IANA timezone: Africa/Bissau', 'Africa', 0.0, NULL),
('Africa/Blantyre', 'Africa/Blantyre', 'IANA timezone: Africa/Blantyre', 'Africa', 2.0, NULL),
('Africa/Brazzaville', 'Africa/Brazzaville', 'IANA timezone: Africa/Brazzaville', 'Africa', 1.0, NULL),
('Africa/Bujumbura', 'Africa/Bujumbura', 'IANA timezone: Africa/Bujumbura', 'Africa', 2.0, NULL),
('Africa/Cairo', 'Africa/Cairo', 'IANA timezone: Africa/Cairo', 'Africa', 3.0, NULL),
('Africa/Casablanca', 'Africa/Casablanca', 'IANA timezone: Africa/Casablanca', 'Africa', 1.0, NULL),
('Africa/Ceuta', 'Africa/Ceuta', 'IANA timezone: Africa/Ceuta', 'Africa', 2.0, NULL),
('Africa/Conakry', 'Africa/Conakry', 'IANA timezone: Africa/Conakry', 'Africa', 0.0, NULL),
('Africa/Dakar', 'Africa/Dakar', 'IANA timezone: Africa/Dakar', 'Africa', 0.0, NULL),
('Africa/Dar_es_Salaam', 'Africa/Dar es Salaam', 'IANA timezone: Africa/Dar_es_Salaam', 'Africa', 3.0, NULL),
('Africa/Djibouti', 'Africa/Djibouti', 'IANA timezone: Africa/Djibouti', 'Africa', 3.0, NULL),
('Africa/Douala', 'Africa/Douala', 'IANA timezone: Africa/Douala', 'Africa', 1.0, NULL),
('Africa/El_Aaiun', 'Africa/El Aaiun', 'IANA timezone: Africa/El_Aaiun', 'Africa', 1.0, NULL),
('Africa/Freetown', 'Africa/Freetown', 'IANA timezone: Africa/Freetown', 'Africa', 0.0, NULL),
('Africa/Gaborone', 'Africa/Gaborone', 'IANA timezone: Africa/Gaborone', 'Africa', 2.0, NULL),
('Africa/Harare', 'Africa/Harare', 'IANA timezone: Africa/Harare', 'Africa', 2.0, NULL),
('Africa/Johannesburg', 'Africa/Johannesburg', 'IANA timezone: Africa/Johannesburg', 'Africa', 2.0, NULL),
('Africa/Juba', 'Africa/Juba', 'IANA timezone: Africa/Juba', 'Africa', 2.0, NULL),
('Africa/Kampala', 'Africa/Kampala', 'IANA timezone: Africa/Kampala', 'Africa', 3.0, NULL),
('Africa/Khartoum', 'Africa/Khartoum', 'IANA timezone: Africa/Khartoum', 'Africa', 2.0, NULL),
('Africa/Kigali', 'Africa/Kigali', 'IANA timezone: Africa/Kigali', 'Africa', 2.0, NULL),
('Africa/Kinshasa', 'Africa/Kinshasa', 'IANA timezone: Africa/Kinshasa', 'Africa', 1.0, NULL),
('Africa/Lagos', 'Africa/Lagos', 'IANA timezone: Africa/Lagos', 'Africa', 1.0, NULL),
('Africa/Libreville', 'Africa/Libreville', 'IANA timezone: Africa/Libreville', 'Africa', 1.0, NULL),
('Africa/Lome', 'Africa/Lome', 'IANA timezone: Africa/Lome', 'Africa', 0.0, NULL),
('Africa/Luanda', 'Africa/Luanda', 'IANA timezone: Africa/Luanda', 'Africa', 1.0, NULL),
('Africa/Lubumbashi', 'Africa/Lubumbashi', 'IANA timezone: Africa/Lubumbashi', 'Africa', 2.0, NULL),
('Africa/Lusaka', 'Africa/Lusaka', 'IANA timezone: Africa/Lusaka', 'Africa', 2.0, NULL),
('Africa/Malabo', 'Africa/Malabo', 'IANA timezone: Africa/Malabo', 'Africa', 1.0, NULL),
('Africa/Maputo', 'Africa/Maputo', 'IANA timezone: Africa/Maputo', 'Africa', 2.0, NULL),
('Africa/Maseru', 'Africa/Maseru', 'IANA timezone: Africa/Maseru', 'Africa', 2.0, NULL),
('Africa/Mbabane', 'Africa/Mbabane', 'IANA timezone: Africa/Mbabane', 'Africa', 2.0, NULL),
('Africa/Mogadishu', 'Africa/Mogadishu', 'IANA timezone: Africa/Mogadishu', 'Africa', 3.0, NULL),
('Africa/Monrovia', 'Africa/Monrovia', 'IANA timezone: Africa/Monrovia', 'Africa', 0.0, NULL),
('Africa/Nairobi', 'Africa/Nairobi', 'IANA timezone: Africa/Nairobi', 'Africa', 3.0, NULL),
('Africa/Ndjamena', 'Africa/Ndjamena', 'IANA timezone: Africa/Ndjamena', 'Africa', 1.0, NULL),
('Africa/Niamey', 'Africa/Niamey', 'IANA timezone: Africa/Niamey', 'Africa', 1.0, NULL),
('Africa/Nouakchott', 'Africa/Nouakchott', 'IANA timezone: Africa/Nouakchott', 'Africa', 0.0, NULL),
('Africa/Ouagadougou', 'Africa/Ouagadougou', 'IANA timezone: Africa/Ouagadougou', 'Africa', 0.0, NULL),
('Africa/Porto-Novo', 'Africa/Porto-Novo', 'IANA timezone: Africa/Porto-Novo', 'Africa', 1.0, NULL),
('Africa/Sao_Tome', 'Africa/Sao Tome', 'IANA timezone: Africa/Sao_Tome', 'Africa', 0.0, NULL),
('Africa/Timbuktu', 'Africa/Timbuktu', 'IANA timezone: Africa/Timbuktu', 'Africa', 0.0, NULL),
('Africa/Tripoli', 'Africa/Tripoli', 'IANA timezone: Africa/Tripoli', 'Africa', 2.0, NULL),
('Africa/Tunis', 'Africa/Tunis', 'IANA timezone: Africa/Tunis', 'Africa', 1.0, NULL),
('Africa/Windhoek', 'Africa/Windhoek', 'IANA timezone: Africa/Windhoek', 'Africa', 2.0, NULL),
('America/Adak', 'America/Adak', 'IANA timezone: America/Adak', 'America', -9.0, NULL),
('America/Anchorage', 'America/Anchorage', 'IANA timezone: America/Anchorage', 'America', -8.0, NULL),
('America/Anguilla', 'America/Anguilla', 'IANA timezone: America/Anguilla', 'America', -4.0, NULL),
('America/Antigua', 'America/Antigua', 'IANA timezone: America/Antigua', 'America', -4.0, NULL),
('America/Araguaina', 'America/Araguaina', 'IANA timezone: America/Araguaina', 'America', -3.0, NULL),
('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos Aires', 'IANA timezone: America/Argentina/Buenos_Aires', 'America', -3.0, NULL),
('America/Argentina/Catamarca', 'America/Argentina/Catamarca', 'IANA timezone: America/Argentina/Catamarca', 'America', -3.0, NULL),
('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia', 'IANA timezone: America/Argentina/ComodRivadavia', 'America', -3.0, NULL),
('America/Argentina/Cordoba', 'America/Argentina/Cordoba', 'IANA timezone: America/Argentina/Cordoba', 'America', -3.0, NULL),
('America/Argentina/Jujuy', 'America/Argentina/Jujuy', 'IANA timezone: America/Argentina/Jujuy', 'America', -3.0, NULL),
('America/Argentina/La_Rioja', 'America/Argentina/La Rioja', 'IANA timezone: America/Argentina/La_Rioja', 'America', -3.0, NULL),
('America/Argentina/Mendoza', 'America/Argentina/Mendoza', 'IANA timezone: America/Argentina/Mendoza', 'America', -3.0, NULL),
('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio Gallegos', 'IANA timezone: America/Argentina/Rio_Gallegos', 'America', -3.0, NULL),
('America/Argentina/Salta', 'America/Argentina/Salta', 'IANA timezone: America/Argentina/Salta', 'America', -3.0, NULL),
('America/Argentina/San_Juan', 'America/Argentina/San Juan', 'IANA timezone: America/Argentina/San_Juan', 'America', -3.0, NULL),
('America/Argentina/San_Luis', 'America/Argentina/San Luis', 'IANA timezone: America/Argentina/San_Luis', 'America', -3.0, NULL),
('America/Argentina/Tucuman', 'America/Argentina/Tucuman', 'IANA timezone: America/Argentina/Tucuman', 'America', -3.0, NULL),
('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia', 'IANA timezone: America/Argentina/Ushuaia', 'America', -3.0, NULL),
('America/Aruba', 'America/Aruba', 'IANA timezone: America/Aruba', 'America', -4.0, NULL),
('America/Asuncion', 'America/Asuncion', 'IANA timezone: America/Asuncion', 'America', -3.0, NULL),
('America/Atikokan', 'America/Atikokan', 'IANA timezone: America/Atikokan', 'America', -5.0, NULL),
('America/Atka', 'America/Atka', 'IANA timezone: America/Atka', 'America', -9.0, NULL),
('America/Bahia', 'America/Bahia', 'IANA timezone: America/Bahia', 'America', -3.0, NULL),
('America/Bahia_Banderas', 'America/Bahia Banderas', 'IANA timezone: America/Bahia_Banderas', 'America', -6.0, NULL),
('America/Barbados', 'America/Barbados', 'IANA timezone: America/Barbados', 'America', -4.0, NULL),
('America/Belem', 'America/Belem', 'IANA timezone: America/Belem', 'America', -3.0, NULL),
('America/Belize', 'America/Belize', 'IANA timezone: America/Belize', 'America', -6.0, NULL),
('America/Blanc-Sablon', 'America/Blanc-Sablon', 'IANA timezone: America/Blanc-Sablon', 'America', -4.0, NULL),
('America/Boa_Vista', 'America/Boa Vista', 'IANA timezone: America/Boa_Vista', 'America', -4.0, NULL),
('America/Bogota', 'America/Bogota', 'IANA timezone: America/Bogota', 'America', -5.0, NULL),
('America/Boise', 'America/Boise', 'IANA timezone: America/Boise', 'America', -6.0, NULL),
('America/Buenos_Aires', 'America/Buenos Aires', 'IANA timezone: America/Buenos_Aires', 'America', -3.0, NULL),
('America/Cambridge_Bay', 'America/Cambridge Bay', 'IANA timezone: America/Cambridge_Bay', 'America', -6.0, NULL),
('America/Campo_Grande', 'America/Campo Grande', 'IANA timezone: America/Campo_Grande', 'America', -4.0, NULL),
('America/Cancun', 'America/Cancun', 'IANA timezone: America/Cancun', 'America', -5.0, NULL),
('America/Caracas', 'America/Caracas', 'IANA timezone: America/Caracas', 'America', -4.0, NULL),
('America/Catamarca', 'America/Catamarca', 'IANA timezone: America/Catamarca', 'America', -3.0, NULL),
('America/Cayenne', 'America/Cayenne', 'IANA timezone: America/Cayenne', 'America', -3.0, NULL),
('America/Cayman', 'America/Cayman', 'IANA timezone: America/Cayman', 'America', -5.0, NULL),
('America/Chicago', 'America/Chicago', 'IANA timezone: America/Chicago', 'America', -5.0, NULL),
('America/Chihuahua', 'America/Chihuahua', 'IANA timezone: America/Chihuahua', 'America', -6.0, NULL),
('America/Ciudad_Juarez', 'America/Ciudad Juarez', 'IANA timezone: America/Ciudad_Juarez', 'America', -6.0, NULL),
('America/Coral_Harbour', 'America/Coral Harbour', 'IANA timezone: America/Coral_Harbour', 'America', -5.0, NULL),
('America/Cordoba', 'America/Cordoba', 'IANA timezone: America/Cordoba', 'America', -3.0, NULL),
('America/Costa_Rica', 'America/Costa Rica', 'IANA timezone: America/Costa_Rica', 'America', -6.0, NULL),
('America/Coyhaique', 'America/Coyhaique', 'IANA timezone: America/Coyhaique', 'America', -3.0, NULL),
('America/Creston', 'America/Creston', 'IANA timezone: America/Creston', 'America', -7.0, NULL),
('America/Cuiaba', 'America/Cuiaba', 'IANA timezone: America/Cuiaba', 'America', -4.0, NULL),
('America/Curacao', 'America/Curacao', 'IANA timezone: America/Curacao', 'America', -4.0, NULL),
('America/Danmarkshavn', 'America/Danmarkshavn', 'IANA timezone: America/Danmarkshavn', 'America', 0.0, NULL),
('America/Dawson', 'America/Dawson', 'IANA timezone: America/Dawson', 'America', -7.0, NULL),
('America/Dawson_Creek', 'America/Dawson Creek', 'IANA timezone: America/Dawson_Creek', 'America', -7.0, NULL),
('America/Denver', 'America/Denver', 'IANA timezone: America/Denver', 'America', -6.0, NULL),
('America/Detroit', 'America/Detroit', 'IANA timezone: America/Detroit', 'America', -4.0, NULL),
('America/Dominica', 'America/Dominica', 'IANA timezone: America/Dominica', 'America', -4.0, NULL),
('America/Edmonton', 'America/Edmonton', 'IANA timezone: America/Edmonton', 'America', -6.0, NULL),
('America/Eirunepe', 'America/Eirunepe', 'IANA timezone: America/Eirunepe', 'America', -5.0, NULL),
('America/El_Salvador', 'America/El Salvador', 'IANA timezone: America/El_Salvador', 'America', -6.0, NULL),
('America/Ensenada', 'America/Ensenada', 'IANA timezone: America/Ensenada', 'America', -7.0, NULL),
('America/Fortaleza', 'America/Fortaleza', 'IANA timezone: America/Fortaleza', 'America', -3.0, NULL),
('America/Fort_Nelson', 'America/Fort Nelson', 'IANA timezone: America/Fort_Nelson', 'America', -7.0, NULL),
('America/Fort_Wayne', 'America/Fort Wayne', 'IANA timezone: America/Fort_Wayne', 'America', -4.0, NULL),
('America/Glace_Bay', 'America/Glace Bay', 'IANA timezone: America/Glace_Bay', 'America', -3.0, NULL),
('America/Godthab', 'America/Godthab', 'IANA timezone: America/Godthab', 'America', -1.0, NULL),
('America/Goose_Bay', 'America/Goose Bay', 'IANA timezone: America/Goose_Bay', 'America', -3.0, NULL),
('America/Grand_Turk', 'America/Grand Turk', 'IANA timezone: America/Grand_Turk', 'America', -4.0, NULL),
('America/Grenada', 'America/Grenada', 'IANA timezone: America/Grenada', 'America', -4.0, NULL),
('America/Guadeloupe', 'America/Guadeloupe', 'IANA timezone: America/Guadeloupe', 'America', -4.0, NULL),
('America/Guatemala', 'America/Guatemala', 'IANA timezone: America/Guatemala', 'America', -6.0, NULL),
('America/Guayaquil', 'America/Guayaquil', 'IANA timezone: America/Guayaquil', 'America', -5.0, NULL),
('America/Guyana', 'America/Guyana', 'IANA timezone: America/Guyana', 'America', -4.0, NULL),
('America/Halifax', 'America/Halifax', 'IANA timezone: America/Halifax', 'America', -3.0, NULL),
('America/Havana', 'America/Havana', 'IANA timezone: America/Havana', 'America', -4.0, NULL),
('America/Hermosillo', 'America/Hermosillo', 'IANA timezone: America/Hermosillo', 'America', -7.0, NULL),
('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis', 'IANA timezone: America/Indiana/Indianapolis', 'America', -4.0, NULL),
('America/Indiana/Knox', 'America/Indiana/Knox', 'IANA timezone: America/Indiana/Knox', 'America', -5.0, NULL),
('America/Indiana/Marengo', 'America/Indiana/Marengo', 'IANA timezone: America/Indiana/Marengo', 'America', -4.0, NULL),
('America/Indiana/Petersburg', 'America/Indiana/Petersburg', 'IANA timezone: America/Indiana/Petersburg', 'America', -4.0, NULL),
('America/Indianapolis', 'America/Indianapolis', 'IANA timezone: America/Indianapolis', 'America', -4.0, NULL),
('America/Indiana/Tell_City', 'America/Indiana/Tell City', 'IANA timezone: America/Indiana/Tell_City', 'America', -5.0, NULL),
('America/Indiana/Vevay', 'America/Indiana/Vevay', 'IANA timezone: America/Indiana/Vevay', 'America', -4.0, NULL),
('America/Indiana/Vincennes', 'America/Indiana/Vincennes', 'IANA timezone: America/Indiana/Vincennes', 'America', -4.0, NULL),
('America/Indiana/Winamac', 'America/Indiana/Winamac', 'IANA timezone: America/Indiana/Winamac', 'America', -4.0, NULL),
('America/Inuvik', 'America/Inuvik', 'IANA timezone: America/Inuvik', 'America', -6.0, NULL),
('America/Iqaluit', 'America/Iqaluit', 'IANA timezone: America/Iqaluit', 'America', -4.0, NULL),
('America/Jamaica', 'America/Jamaica', 'IANA timezone: America/Jamaica', 'America', -5.0, NULL),
('America/Jujuy', 'America/Jujuy', 'IANA timezone: America/Jujuy', 'America', -3.0, NULL),
('America/Juneau', 'America/Juneau', 'IANA timezone: America/Juneau', 'America', -8.0, NULL),
('America/Kentucky/Louisville', 'America/Kentucky/Louisville', 'IANA timezone: America/Kentucky/Louisville', 'America', -4.0, NULL),
('America/Kentucky/Monticello', 'America/Kentucky/Monticello', 'IANA timezone: America/Kentucky/Monticello', 'America', -4.0, NULL),
('America/Knox_IN', 'America/Knox IN', 'IANA timezone: America/Knox_IN', 'America', -5.0, NULL),
('America/Kralendijk', 'America/Kralendijk', 'IANA timezone: America/Kralendijk', 'America', -4.0, NULL),
('America/La_Paz', 'America/La Paz', 'IANA timezone: America/La_Paz', 'America', -4.0, NULL),
('America/Lima', 'America/Lima', 'IANA timezone: America/Lima', 'America', -5.0, NULL),
('America/Los_Angeles', 'America/Los Angeles', 'IANA timezone: America/Los_Angeles', 'America', -7.0, NULL),
('America/Louisville', 'America/Louisville', 'IANA timezone: America/Louisville', 'America', -4.0, NULL),
('America/Lower_Princes', 'America/Lower Princes', 'IANA timezone: America/Lower_Princes', 'America', -4.0, NULL),
('America/Maceio', 'America/Maceio', 'IANA timezone: America/Maceio', 'America', -3.0, NULL),
('America/Managua', 'America/Managua', 'IANA timezone: America/Managua', 'America', -6.0, NULL),
('America/Manaus', 'America/Manaus', 'IANA timezone: America/Manaus', 'America', -4.0, NULL),
('America/Marigot', 'America/Marigot', 'IANA timezone: America/Marigot', 'America', -4.0, NULL),
('America/Martinique', 'America/Martinique', 'IANA timezone: America/Martinique', 'America', -4.0, NULL),
('America/Matamoros', 'America/Matamoros', 'IANA timezone: America/Matamoros', 'America', -5.0, NULL),
('America/Mazatlan', 'America/Mazatlan', 'IANA timezone: America/Mazatlan', 'America', -7.0, NULL),
('America/Mendoza', 'America/Mendoza', 'IANA timezone: America/Mendoza', 'America', -3.0, NULL),
('America/Menominee', 'America/Menominee', 'IANA timezone: America/Menominee', 'America', -5.0, NULL),
('America/Merida', 'America/Merida', 'IANA timezone: America/Merida', 'America', -6.0, NULL),
('America/Metlakatla', 'America/Metlakatla', 'IANA timezone: America/Metlakatla', 'America', -8.0, NULL),
('America/Mexico_City', 'America/Mexico City', 'IANA timezone: America/Mexico_City', 'America', -6.0, NULL),
('America/Miquelon', 'America/Miquelon', 'IANA timezone: America/Miquelon', 'America', -2.0, NULL),
('America/Moncton', 'America/Moncton', 'IANA timezone: America/Moncton', 'America', -3.0, NULL),
('America/Monterrey', 'America/Monterrey', 'IANA timezone: America/Monterrey', 'America', -6.0, NULL),
('America/Montevideo', 'America/Montevideo', 'IANA timezone: America/Montevideo', 'America', -3.0, NULL),
('America/Montreal', 'America/Montreal', 'IANA timezone: America/Montreal', 'America', -4.0, NULL),
('America/Montserrat', 'America/Montserrat', 'IANA timezone: America/Montserrat', 'America', -4.0, NULL),
('America/Nassau', 'America/Nassau', 'IANA timezone: America/Nassau', 'America', -4.0, NULL),
('America/New_York', 'America/New York', 'IANA timezone: America/New_York', 'America', -4.0, NULL),
('America/Nipigon', 'America/Nipigon', 'IANA timezone: America/Nipigon', 'America', -4.0, NULL),
('America/Nome', 'America/Nome', 'IANA timezone: America/Nome', 'America', -8.0, NULL),
('America/Noronha', 'America/Noronha', 'IANA timezone: America/Noronha', 'America', -2.0, NULL),
('America/North_Dakota/Beulah', 'America/North Dakota/Beulah', 'IANA timezone: America/North_Dakota/Beulah', 'America', -5.0, NULL),
('America/North_Dakota/Center', 'America/North Dakota/Center', 'IANA timezone: America/North_Dakota/Center', 'America', -5.0, NULL),
('America/North_Dakota/New_Salem', 'America/North Dakota/New Salem', 'IANA timezone: America/North_Dakota/New_Salem', 'America', -5.0, NULL),
('America/Nuuk', 'America/Nuuk', 'IANA timezone: America/Nuuk', 'America', -1.0, NULL),
('America/Ojinaga', 'America/Ojinaga', 'IANA timezone: America/Ojinaga', 'America', -5.0, NULL),
('America/Panama', 'America/Panama', 'IANA timezone: America/Panama', 'America', -5.0, NULL),
('America/Pangnirtung', 'America/Pangnirtung', 'IANA timezone: America/Pangnirtung', 'America', -4.0, NULL),
('America/Paramaribo', 'America/Paramaribo', 'IANA timezone: America/Paramaribo', 'America', -3.0, NULL),
('America/Phoenix', 'America/Phoenix', 'IANA timezone: America/Phoenix', 'America', -7.0, NULL),
('America/Port-au-Prince', 'America/Port-au-Prince', 'IANA timezone: America/Port-au-Prince', 'America', -4.0, NULL),
('America/Porto_Acre', 'America/Porto Acre', 'IANA timezone: America/Porto_Acre', 'America', -5.0, NULL),
('America/Port_of_Spain', 'America/Port of Spain', 'IANA timezone: America/Port_of_Spain', 'America', -4.0, NULL),
('America/Porto_Velho', 'America/Porto Velho', 'IANA timezone: America/Porto_Velho', 'America', -4.0, NULL),
('America/Puerto_Rico', 'America/Puerto Rico', 'IANA timezone: America/Puerto_Rico', 'America', -4.0, NULL),
('America/Punta_Arenas', 'America/Punta Arenas', 'IANA timezone: America/Punta_Arenas', 'America', -3.0, NULL),
('America/Rainy_River', 'America/Rainy River', 'IANA timezone: America/Rainy_River', 'America', -5.0, NULL),
('America/Rankin_Inlet', 'America/Rankin Inlet', 'IANA timezone: America/Rankin_Inlet', 'America', -5.0, NULL),
('America/Recife', 'America/Recife', 'IANA timezone: America/Recife', 'America', -3.0, NULL),
('America/Regina', 'America/Regina', 'IANA timezone: America/Regina', 'America', -6.0, NULL),
('America/Resolute', 'America/Resolute', 'IANA timezone: America/Resolute', 'America', -5.0, NULL),
('America/Rio_Branco', 'America/Rio Branco', 'IANA timezone: America/Rio_Branco', 'America', -5.0, NULL),
('America/Rosario', 'America/Rosario', 'IANA timezone: America/Rosario', 'America', -3.0, NULL),
('America/Santa_Isabel', 'America/Santa Isabel', 'IANA timezone: America/Santa_Isabel', 'America', -7.0, NULL),
('America/Santarem', 'America/Santarem', 'IANA timezone: America/Santarem', 'America', -3.0, NULL),
('America/Santiago', 'America/Santiago', 'IANA timezone: America/Santiago', 'America', -3.0, NULL),
('America/Santo_Domingo', 'America/Santo Domingo', 'IANA timezone: America/Santo_Domingo', 'America', -4.0, NULL),
('America/Sao_Paulo', 'America/Sao Paulo', 'IANA timezone: America/Sao_Paulo', 'America', -3.0, NULL),
('America/Scoresbysund', 'America/Scoresbysund', 'IANA timezone: America/Scoresbysund', 'America', -1.0, NULL),
('America/Shiprock', 'America/Shiprock', 'IANA timezone: America/Shiprock', 'America', -6.0, NULL),
('America/Sitka', 'America/Sitka', 'IANA timezone: America/Sitka', 'America', -8.0, NULL),
('America/St_Barthelemy', 'America/St Barthelemy', 'IANA timezone: America/St_Barthelemy', 'America', -4.0, NULL),
('America/St_Johns', 'America/St Johns', 'IANA timezone: America/St_Johns', 'America', -2.5, NULL),
('America/St_Kitts', 'America/St Kitts', 'IANA timezone: America/St_Kitts', 'America', -4.0, NULL),
('America/St_Lucia', 'America/St Lucia', 'IANA timezone: America/St_Lucia', 'America', -4.0, NULL),
('America/St_Thomas', 'America/St Thomas', 'IANA timezone: America/St_Thomas', 'America', -4.0, NULL),
('America/St_Vincent', 'America/St Vincent', 'IANA timezone: America/St_Vincent', 'America', -4.0, NULL),
('America/Swift_Current', 'America/Swift Current', 'IANA timezone: America/Swift_Current', 'America', -6.0, NULL),
('America/Tegucigalpa', 'America/Tegucigalpa', 'IANA timezone: America/Tegucigalpa', 'America', -6.0, NULL),
('America/Thule', 'America/Thule', 'IANA timezone: America/Thule', 'America', -3.0, NULL),
('America/Thunder_Bay', 'America/Thunder Bay', 'IANA timezone: America/Thunder_Bay', 'America', -4.0, NULL),
('America/Tijuana', 'America/Tijuana', 'IANA timezone: America/Tijuana', 'America', -7.0, NULL),
('America/Toronto', 'America/Toronto', 'IANA timezone: America/Toronto', 'America', -4.0, NULL),
('America/Tortola', 'America/Tortola', 'IANA timezone: America/Tortola', 'America', -4.0, NULL),
('America/Vancouver', 'America/Vancouver', 'IANA timezone: America/Vancouver', 'America', -7.0, NULL),
('America/Virgin', 'America/Virgin', 'IANA timezone: America/Virgin', 'America', -4.0, NULL),
('America/Whitehorse', 'America/Whitehorse', 'IANA timezone: America/Whitehorse', 'America', -7.0, NULL),
('America/Winnipeg', 'America/Winnipeg', 'IANA timezone: America/Winnipeg', 'America', -5.0, NULL),
('America/Yakutat', 'America/Yakutat', 'IANA timezone: America/Yakutat', 'America', -8.0, NULL),
('America/Yellowknife', 'America/Yellowknife', 'IANA timezone: America/Yellowknife', 'America', -6.0, NULL),
('Antarctica/Casey', 'Antarctica/Casey', 'IANA timezone: Antarctica/Casey', 'Antarctica', 8.0, NULL),
('Antarctica/Davis', 'Antarctica/Davis', 'IANA timezone: Antarctica/Davis', 'Antarctica', 7.0, NULL),
('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville', 'IANA timezone: Antarctica/DumontDUrville', 'Antarctica', 10.0, NULL),
('Antarctica/Macquarie', 'Antarctica/Macquarie', 'IANA timezone: Antarctica/Macquarie', 'Antarctica', 11.0, NULL),
('Antarctica/Mawson', 'Antarctica/Mawson', 'IANA timezone: Antarctica/Mawson', 'Antarctica', 5.0, NULL),
('Antarctica/McMurdo', 'Antarctica/McMurdo', 'IANA timezone: Antarctica/McMurdo', 'Antarctica', 13.0, NULL),
('Antarctica/Palmer', 'Antarctica/Palmer', 'IANA timezone: Antarctica/Palmer', 'Antarctica', -3.0, NULL),
('Antarctica/Rothera', 'Antarctica/Rothera', 'IANA timezone: Antarctica/Rothera', 'Antarctica', -3.0, NULL),
('Antarctica/South_Pole', 'Antarctica/South Pole', 'IANA timezone: Antarctica/South_Pole', 'Antarctica', 13.0, NULL),
('Antarctica/Syowa', 'Antarctica/Syowa', 'IANA timezone: Antarctica/Syowa', 'Antarctica', 3.0, NULL),
('Antarctica/Troll', 'Antarctica/Troll', 'IANA timezone: Antarctica/Troll', 'Antarctica', 2.0, NULL),
('Antarctica/Vostok', 'Antarctica/Vostok', 'IANA timezone: Antarctica/Vostok', 'Antarctica', 5.0, NULL),
('Arctic/Longyearbyen', 'Arctic/Longyearbyen', 'IANA timezone: Arctic/Longyearbyen', 'Arctic', 2.0, NULL),
('Asia/Aden', 'Asia/Aden', 'IANA timezone: Asia/Aden', 'Asia', 3.0, NULL),
('Asia/Almaty', 'Asia/Almaty', 'IANA timezone: Asia/Almaty', 'Asia', 5.0, NULL),
('Asia/Amman', 'Asia/Amman', 'IANA timezone: Asia/Amman', 'Asia', 3.0, NULL),
('Asia/Anadyr', 'Asia/Anadyr', 'IANA timezone: Asia/Anadyr', 'Asia', 12.0, NULL),
('Asia/Aqtau', 'Asia/Aqtau', 'IANA timezone: Asia/Aqtau', 'Asia', 5.0, NULL),
('Asia/Aqtobe', 'Asia/Aqtobe', 'IANA timezone: Asia/Aqtobe', 'Asia', 5.0, NULL),
('Asia/Ashgabat', 'Asia/Ashgabat', 'IANA timezone: Asia/Ashgabat', 'Asia', 5.0, NULL),
('Asia/Ashkhabad', 'Asia/Ashkhabad', 'IANA timezone: Asia/Ashkhabad', 'Asia', 5.0, NULL),
('Asia/Atyrau', 'Asia/Atyrau', 'IANA timezone: Asia/Atyrau', 'Asia', 5.0, NULL),
('Asia/Baghdad', 'Asia/Baghdad', 'IANA timezone: Asia/Baghdad', 'Asia', 3.0, NULL),
('Asia/Bahrain', 'Asia/Bahrain', 'IANA timezone: Asia/Bahrain', 'Asia', 3.0, NULL),
('Asia/Baku', 'Asia/Baku', 'IANA timezone: Asia/Baku', 'Asia', 4.0, NULL),
('Asia/Bangkok', 'Asia/Bangkok', 'IANA timezone: Asia/Bangkok', 'Asia', 7.0, NULL),
('Asia/Barnaul', 'Asia/Barnaul', 'IANA timezone: Asia/Barnaul', 'Asia', 7.0, NULL),
('Asia/Beirut', 'Asia/Beirut', 'IANA timezone: Asia/Beirut', 'Asia', 3.0, NULL),
('Asia/Bishkek', 'Asia/Bishkek', 'IANA timezone: Asia/Bishkek', 'Asia', 6.0, NULL),
('Asia/Brunei', 'Asia/Brunei', 'IANA timezone: Asia/Brunei', 'Asia', 8.0, NULL),
('Asia/Calcutta', 'Asia/Calcutta', 'IANA timezone: Asia/Calcutta', 'Asia', 5.5, NULL),
('Asia/Chita', 'Asia/Chita', 'IANA timezone: Asia/Chita', 'Asia', 9.0, NULL),
('Asia/Choibalsan', 'Asia/Choibalsan', 'IANA timezone: Asia/Choibalsan', 'Asia', 8.0, NULL),
('Asia/Chongqing', 'Asia/Chongqing', 'IANA timezone: Asia/Chongqing', 'Asia', 8.0, NULL),
('Asia/Chungking', 'Asia/Chungking', 'IANA timezone: Asia/Chungking', 'Asia', 8.0, NULL),
('Asia/Colombo', 'Asia/Colombo', 'IANA timezone: Asia/Colombo', 'Asia', 5.5, NULL),
('Asia/Dacca', 'Asia/Dacca', 'IANA timezone: Asia/Dacca', 'Asia', 6.0, NULL),
('Asia/Damascus', 'Asia/Damascus', 'IANA timezone: Asia/Damascus', 'Asia', 3.0, NULL),
('Asia/Dhaka', 'Asia/Dhaka', 'IANA timezone: Asia/Dhaka', 'Asia', 6.0, NULL),
('Asia/Dili', 'Asia/Dili', 'IANA timezone: Asia/Dili', 'Asia', 9.0, NULL),
('Asia/Dubai', 'Asia/Dubai', 'IANA timezone: Asia/Dubai', 'Asia', 4.0, NULL),
('Asia/Dushanbe', 'Asia/Dushanbe', 'IANA timezone: Asia/Dushanbe', 'Asia', 5.0, NULL),
('Asia/Famagusta', 'Asia/Famagusta', 'IANA timezone: Asia/Famagusta', 'Asia', 3.0, NULL),
('Asia/Gaza', 'Asia/Gaza', 'IANA timezone: Asia/Gaza', 'Asia', 3.0, NULL),
('Asia/Harbin', 'Asia/Harbin', 'IANA timezone: Asia/Harbin', 'Asia', 8.0, NULL),
('Asia/Hebron', 'Asia/Hebron', 'IANA timezone: Asia/Hebron', 'Asia', 3.0, NULL),
('Asia/Ho_Chi_Minh', 'Asia/Ho Chi Minh', 'IANA timezone: Asia/Ho_Chi_Minh', 'Asia', 7.0, NULL),
('Asia/Hong_Kong', 'Asia/Hong Kong', 'IANA timezone: Asia/Hong_Kong', 'Asia', 8.0, NULL),
('Asia/Hovd', 'Asia/Hovd', 'IANA timezone: Asia/Hovd', 'Asia', 7.0, NULL),
('Asia/Irkutsk', 'Asia/Irkutsk', 'IANA timezone: Asia/Irkutsk', 'Asia', 8.0, NULL),
('Asia/Istanbul', 'Asia/Istanbul', 'IANA timezone: Asia/Istanbul', 'Asia', 3.0, NULL),
('Asia/Jakarta', 'Asia/Jakarta', 'IANA timezone: Asia/Jakarta', 'Asia', 7.0, NULL),
('Asia/Jayapura', 'Asia/Jayapura', 'IANA timezone: Asia/Jayapura', 'Asia', 9.0, NULL),
('Asia/Jerusalem', 'Asia/Jerusalem', 'IANA timezone: Asia/Jerusalem', 'Asia', 3.0, NULL),
('Asia/Kabul', 'Asia/Kabul', 'IANA timezone: Asia/Kabul', 'Asia', 4.5, NULL),
('Asia/Kamchatka', 'Asia/Kamchatka', 'IANA timezone: Asia/Kamchatka', 'Asia', 12.0, NULL),
('Asia/Karachi', 'Asia/Karachi', 'IANA timezone: Asia/Karachi', 'Asia', 5.0, NULL),
('Asia/Kashgar', 'Asia/Kashgar', 'IANA timezone: Asia/Kashgar', 'Asia', 6.0, NULL),
('Asia/Kathmandu', 'Asia/Kathmandu', 'IANA timezone: Asia/Kathmandu', 'Asia', 5.75, NULL),
('Asia/Katmandu', 'Asia/Katmandu', 'IANA timezone: Asia/Katmandu', 'Asia', 5.75, NULL),
('Asia/Khandyga', 'Asia/Khandyga', 'IANA timezone: Asia/Khandyga', 'Asia', 9.0, NULL),
('Asia/Kolkata', 'Asia/Kolkata', 'IANA timezone: Asia/Kolkata', 'Asia', 5.5, NULL),
('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk', 'IANA timezone: Asia/Krasnoyarsk', 'Asia', 7.0, NULL),
('Asia/Kuala_Lumpur', 'Asia/Kuala Lumpur', 'IANA timezone: Asia/Kuala_Lumpur', 'Asia', 8.0, NULL),
('Asia/Kuching', 'Asia/Kuching', 'IANA timezone: Asia/Kuching', 'Asia', 8.0, NULL),
('Asia/Kuwait', 'Asia/Kuwait', 'IANA timezone: Asia/Kuwait', 'Asia', 3.0, NULL),
('Asia/Macao', 'Asia/Macao', 'IANA timezone: Asia/Macao', 'Asia', 8.0, NULL),
('Asia/Macau', 'Asia/Macau', 'IANA timezone: Asia/Macau', 'Asia', 8.0, NULL),
('Asia/Magadan', 'Asia/Magadan', 'IANA timezone: Asia/Magadan', 'Asia', 11.0, NULL),
('Asia/Makassar', 'Asia/Makassar', 'IANA timezone: Asia/Makassar', 'Asia', 8.0, NULL),
('Asia/Manila', 'Asia/Manila', 'IANA timezone: Asia/Manila', 'Asia', 8.0, NULL),
('Asia/Muscat', 'Asia/Muscat', 'IANA timezone: Asia/Muscat', 'Asia', 4.0, NULL),
('Asia/Nicosia', 'Asia/Nicosia', 'IANA timezone: Asia/Nicosia', 'Asia', 3.0, NULL),
('Asia/Novokuznetsk', 'Asia/Novokuznetsk', 'IANA timezone: Asia/Novokuznetsk', 'Asia', 7.0, NULL),
('Asia/Novosibirsk', 'Asia/Novosibirsk', 'IANA timezone: Asia/Novosibirsk', 'Asia', 7.0, NULL),
('Asia/Omsk', 'Asia/Omsk', 'IANA timezone: Asia/Omsk', 'Asia', 6.0, NULL),
('Asia/Oral', 'Asia/Oral', 'IANA timezone: Asia/Oral', 'Asia', 5.0, NULL),
('Asia/Phnom_Penh', 'Asia/Phnom Penh', 'IANA timezone: Asia/Phnom_Penh', 'Asia', 7.0, NULL),
('Asia/Pontianak', 'Asia/Pontianak', 'IANA timezone: Asia/Pontianak', 'Asia', 7.0, NULL),
('Asia/Pyongyang', 'Asia/Pyongyang', 'IANA timezone: Asia/Pyongyang', 'Asia', 9.0, NULL),
('Asia/Qatar', 'Asia/Qatar', 'IANA timezone: Asia/Qatar', 'Asia', 3.0, NULL),
('Asia/Qostanay', 'Asia/Qostanay', 'IANA timezone: Asia/Qostanay', 'Asia', 5.0, NULL),
('Asia/Qyzylorda', 'Asia/Qyzylorda', 'IANA timezone: Asia/Qyzylorda', 'Asia', 5.0, NULL),
('Asia/Rangoon', 'Asia/Rangoon', 'IANA timezone: Asia/Rangoon', 'Asia', 6.5, NULL),
('Asia/Riyadh', 'Asia/Riyadh', 'IANA timezone: Asia/Riyadh', 'Asia', 3.0, NULL),
('Asia/Saigon', 'Asia/Saigon', 'IANA timezone: Asia/Saigon', 'Asia', 7.0, NULL),
('Asia/Sakhalin', 'Asia/Sakhalin', 'IANA timezone: Asia/Sakhalin', 'Asia', 11.0, NULL),
('Asia/Samarkand', 'Asia/Samarkand', 'IANA timezone: Asia/Samarkand', 'Asia', 5.0, NULL),
('Asia/Seoul', 'Asia/Seoul', 'IANA timezone: Asia/Seoul', 'Asia', 9.0, NULL),
('Asia/Shanghai', 'Asia/Shanghai', 'IANA timezone: Asia/Shanghai', 'Asia', 8.0, NULL),
('Asia/Singapore', 'Asia/Singapore', 'IANA timezone: Asia/Singapore', 'Asia', 8.0, NULL),
('Asia/Srednekolymsk', 'Asia/Srednekolymsk', 'IANA timezone: Asia/Srednekolymsk', 'Asia', 11.0, NULL),
('Asia/Taipei', 'Asia/Taipei', 'IANA timezone: Asia/Taipei', 'Asia', 8.0, NULL),
('Asia/Tashkent', 'Asia/Tashkent', 'IANA timezone: Asia/Tashkent', 'Asia', 5.0, NULL),
('Asia/Tbilisi', 'Asia/Tbilisi', 'IANA timezone: Asia/Tbilisi', 'Asia', 4.0, NULL),
('Asia/Tehran', 'Asia/Tehran', 'IANA timezone: Asia/Tehran', 'Asia', 3.5, NULL),
('Asia/Tel_Aviv', 'Asia/Tel Aviv', 'IANA timezone: Asia/Tel_Aviv', 'Asia', 3.0, NULL),
('Asia/Thimbu', 'Asia/Thimbu', 'IANA timezone: Asia/Thimbu', 'Asia', 6.0, NULL),
('Asia/Thimphu', 'Asia/Thimphu', 'IANA timezone: Asia/Thimphu', 'Asia', 6.0, NULL),
('Asia/Tokyo', 'Asia/Tokyo', 'IANA timezone: Asia/Tokyo', 'Asia', 9.0, NULL),
('Asia/Tomsk', 'Asia/Tomsk', 'IANA timezone: Asia/Tomsk', 'Asia', 7.0, NULL),
('Asia/Ujung_Pandang', 'Asia/Ujung Pandang', 'IANA timezone: Asia/Ujung_Pandang', 'Asia', 8.0, NULL),
('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar', 'IANA timezone: Asia/Ulaanbaatar', 'Asia', 8.0, NULL),
('Asia/Ulan_Bator', 'Asia/Ulan Bator', 'IANA timezone: Asia/Ulan_Bator', 'Asia', 8.0, NULL),
('Asia/Urumqi', 'Asia/Urumqi', 'IANA timezone: Asia/Urumqi', 'Asia', 6.0, NULL),
('Asia/Ust-Nera', 'Asia/Ust-Nera', 'IANA timezone: Asia/Ust-Nera', 'Asia', 10.0, NULL),
('Asia/Vientiane', 'Asia/Vientiane', 'IANA timezone: Asia/Vientiane', 'Asia', 7.0, NULL),
('Asia/Vladivostok', 'Asia/Vladivostok', 'IANA timezone: Asia/Vladivostok', 'Asia', 10.0, NULL),
('Asia/Yakutsk', 'Asia/Yakutsk', 'IANA timezone: Asia/Yakutsk', 'Asia', 9.0, NULL),
('Asia/Yangon', 'Asia/Yangon', 'IANA timezone: Asia/Yangon', 'Asia', 6.5, NULL),
('Asia/Yekaterinburg', 'Asia/Yekaterinburg', 'IANA timezone: Asia/Yekaterinburg', 'Asia', 5.0, NULL),
('Asia/Yerevan', 'Asia/Yerevan', 'IANA timezone: Asia/Yerevan', 'Asia', 4.0, NULL),
('Atlantic/Azores', 'Atlantic/Azores', 'IANA timezone: Atlantic/Azores', 'Atlantic', 0.0, NULL),
('Atlantic/Bermuda', 'Atlantic/Bermuda', 'IANA timezone: Atlantic/Bermuda', 'Atlantic', -3.0, NULL),
('Atlantic/Canary', 'Atlantic/Canary', 'IANA timezone: Atlantic/Canary', 'Atlantic', 1.0, NULL),
('Atlantic/Cape_Verde', 'Atlantic/Cape Verde', 'IANA timezone: Atlantic/Cape_Verde', 'Atlantic', -1.0, NULL),
('Atlantic/Faeroe', 'Atlantic/Faeroe', 'IANA timezone: Atlantic/Faeroe', 'Atlantic', 1.0, NULL),
('Atlantic/Faroe', 'Atlantic/Faroe', 'IANA timezone: Atlantic/Faroe', 'Atlantic', 1.0, NULL),
('Atlantic/Jan_Mayen', 'Atlantic/Jan Mayen', 'IANA timezone: Atlantic/Jan_Mayen', 'Atlantic', 2.0, NULL),
('Atlantic/Madeira', 'Atlantic/Madeira', 'IANA timezone: Atlantic/Madeira', 'Atlantic', 1.0, NULL),
('Atlantic/Reykjavik', 'Atlantic/Reykjavik', 'IANA timezone: Atlantic/Reykjavik', 'Atlantic', 0.0, NULL),
('Atlantic/South_Georgia', 'Atlantic/South Georgia', 'IANA timezone: Atlantic/South_Georgia', 'Atlantic', -2.0, NULL),
('Atlantic/Stanley', 'Atlantic/Stanley', 'IANA timezone: Atlantic/Stanley', 'Atlantic', -3.0, NULL),
('Atlantic/St_Helena', 'Atlantic/St Helena', 'IANA timezone: Atlantic/St_Helena', 'Atlantic', 0.0, NULL),
('Australia/ACT', 'Australia/ACT', 'IANA timezone: Australia/ACT', 'Australia', 11.0, NULL),
('Australia/Adelaide', 'Australia/Adelaide', 'IANA timezone: Australia/Adelaide', 'Australia', 10.5, NULL),
('Australia/Brisbane', 'Australia/Brisbane', 'IANA timezone: Australia/Brisbane', 'Australia', 10.0, NULL),
('Australia/Broken_Hill', 'Australia/Broken Hill', 'IANA timezone: Australia/Broken_Hill', 'Australia', 10.5, NULL),
('Australia/Canberra', 'Australia/Canberra', 'IANA timezone: Australia/Canberra', 'Australia', 11.0, NULL),
('Australia/Currie', 'Australia/Currie', 'IANA timezone: Australia/Currie', 'Australia', 11.0, NULL),
('Australia/Darwin', 'Australia/Darwin', 'IANA timezone: Australia/Darwin', 'Australia', 9.5, NULL),
('Australia/Eucla', 'Australia/Eucla', 'IANA timezone: Australia/Eucla', 'Australia', 8.75, NULL),
('Australia/Hobart', 'Australia/Hobart', 'IANA timezone: Australia/Hobart', 'Australia', 11.0, NULL),
('Australia/LHI', 'Australia/LHI', 'IANA timezone: Australia/LHI', 'Australia', 11.0, NULL),
('Australia/Lindeman', 'Australia/Lindeman', 'IANA timezone: Australia/Lindeman', 'Australia', 10.0, NULL),
('Australia/Lord_Howe', 'Australia/Lord Howe', 'IANA timezone: Australia/Lord_Howe', 'Australia', 11.0, NULL),
('Australia/Melbourne', 'Australia/Melbourne', 'IANA timezone: Australia/Melbourne', 'Australia', 11.0, NULL),
('Australia/North', 'Australia/North', 'IANA timezone: Australia/North', 'Australia', 9.5, NULL),
('Australia/NSW', 'Australia/NSW', 'IANA timezone: Australia/NSW', 'Australia', 11.0, NULL),
('Australia/Perth', 'Australia/Perth', 'IANA timezone: Australia/Perth', 'Australia', 8.0, NULL),
('Australia/Queensland', 'Australia/Queensland', 'IANA timezone: Australia/Queensland', 'Australia', 10.0, NULL),
('Australia/South', 'Australia/South', 'IANA timezone: Australia/South', 'Australia', 10.5, NULL),
('Australia/Sydney', 'Australia/Sydney', 'IANA timezone: Australia/Sydney', 'Australia', 11.0, NULL),
('Australia/Tasmania', 'Australia/Tasmania', 'IANA timezone: Australia/Tasmania', 'Australia', 11.0, NULL),
('Australia/Victoria', 'Australia/Victoria', 'IANA timezone: Australia/Victoria', 'Australia', 11.0, NULL),
('Australia/West', 'Australia/West', 'IANA timezone: Australia/West', 'Australia', 8.0, NULL),
('Australia/Yancowinna', 'Australia/Yancowinna', 'IANA timezone: Australia/Yancowinna', 'Australia', 10.5, NULL),
('Brazil/Acre', 'Brazil/Acre', 'IANA timezone: Brazil/Acre', 'Brazil', -5.0, NULL),
('Brazil/DeNoronha', 'Brazil/DeNoronha', 'IANA timezone: Brazil/DeNoronha', 'Brazil', -2.0, NULL),
('Brazil/East', 'Brazil/East', 'IANA timezone: Brazil/East', 'Brazil', -3.0, NULL),
('Brazil/West', 'Brazil/West', 'IANA timezone: Brazil/West', 'Brazil', -4.0, NULL),
('Canada/Atlantic', 'Canada/Atlantic', 'IANA timezone: Canada/Atlantic', 'Canada', -3.0, NULL),
('Canada/Central', 'Canada/Central', 'IANA timezone: Canada/Central', 'Canada', -5.0, NULL),
('Canada/Eastern', 'Canada/Eastern', 'IANA timezone: Canada/Eastern', 'Canada', -4.0, NULL),
('Canada/Mountain', 'Canada/Mountain', 'IANA timezone: Canada/Mountain', 'Canada', -6.0, NULL),
('Canada/Newfoundland', 'Canada/Newfoundland', 'IANA timezone: Canada/Newfoundland', 'Canada', -2.5, NULL),
('Canada/Pacific', 'Canada/Pacific', 'IANA timezone: Canada/Pacific', 'Canada', -7.0, NULL),
('Canada/Saskatchewan', 'Canada/Saskatchewan', 'IANA timezone: Canada/Saskatchewan', 'Canada', -6.0, NULL),
('Canada/Yukon', 'Canada/Yukon', 'IANA timezone: Canada/Yukon', 'Canada', -7.0, NULL),
('CET', 'CET', 'IANA timezone: CET', 'Other', 2.0, NULL),
('Chile/Continental', 'Chile/Continental', 'IANA timezone: Chile/Continental', 'Chile', -3.0, NULL),
('Chile/EasterIsland', 'Chile/EasterIsland', 'IANA timezone: Chile/EasterIsland', 'Chile', -5.0, NULL),
('CST6CDT', 'CST6CDT', 'IANA timezone: CST6CDT', 'Other', -5.0, NULL),
('Cuba', 'Cuba', 'IANA timezone: Cuba', 'Other', -4.0, NULL),
('EET', 'EET', 'IANA timezone: EET', 'Other', 3.0, NULL),
('Egypt', 'Egypt', 'IANA timezone: Egypt', 'Other', 3.0, NULL),
('Eire', 'Eire', 'IANA timezone: Eire', 'Other', 1.0, NULL),
('EST', 'EST', 'IANA timezone: EST', 'Other', -5.0, NULL),
('EST5EDT', 'EST5EDT', 'IANA timezone: EST5EDT', 'Other', -4.0, NULL),
('Europe/Amsterdam', 'Europe/Amsterdam', 'IANA timezone: Europe/Amsterdam', 'Europe', 2.0, NULL),
('Europe/Andorra', 'Europe/Andorra', 'IANA timezone: Europe/Andorra', 'Europe', 2.0, NULL),
('Europe/Astrakhan', 'Europe/Astrakhan', 'IANA timezone: Europe/Astrakhan', 'Europe', 4.0, NULL),
('Europe/Athens', 'Europe/Athens', 'IANA timezone: Europe/Athens', 'Europe', 3.0, NULL),
('Europe/Belfast', 'Europe/Belfast', 'IANA timezone: Europe/Belfast', 'Europe', 1.0, NULL),
('Europe/Belgrade', 'Europe/Belgrade', 'IANA timezone: Europe/Belgrade', 'Europe', 2.0, NULL),
('Europe/Berlin', 'Europe/Berlin', 'IANA timezone: Europe/Berlin', 'Europe', 2.0, NULL),
('Europe/Bratislava', 'Europe/Bratislava', 'IANA timezone: Europe/Bratislava', 'Europe', 2.0, NULL),
('Europe/Brussels', 'Europe/Brussels', 'IANA timezone: Europe/Brussels', 'Europe', 2.0, NULL),
('Europe/Bucharest', 'Europe/Bucharest', 'IANA timezone: Europe/Bucharest', 'Europe', 3.0, NULL),
('Europe/Budapest', 'Europe/Budapest', 'IANA timezone: Europe/Budapest', 'Europe', 2.0, NULL),
('Europe/Busingen', 'Europe/Busingen', 'IANA timezone: Europe/Busingen', 'Europe', 2.0, NULL),
('Europe/Chisinau', 'Europe/Chisinau', 'IANA timezone: Europe/Chisinau', 'Europe', 3.0, NULL),
('Europe/Copenhagen', 'Europe/Copenhagen', 'IANA timezone: Europe/Copenhagen', 'Europe', 2.0, NULL),
('Europe/Dublin', 'Europe/Dublin', 'IANA timezone: Europe/Dublin', 'Europe', 1.0, NULL),
('Europe/Gibraltar', 'Europe/Gibraltar', 'IANA timezone: Europe/Gibraltar', 'Europe', 2.0, NULL),
('Europe/Guernsey', 'Europe/Guernsey', 'IANA timezone: Europe/Guernsey', 'Europe', 1.0, NULL),
('Europe/Helsinki', 'Europe/Helsinki', 'IANA timezone: Europe/Helsinki', 'Europe', 3.0, NULL),
('Europe/Isle_of_Man', 'Europe/Isle of Man', 'IANA timezone: Europe/Isle_of_Man', 'Europe', 1.0, NULL),
('Europe/Istanbul', 'Europe/Istanbul', 'IANA timezone: Europe/Istanbul', 'Europe', 3.0, NULL),
('Europe/Jersey', 'Europe/Jersey', 'IANA timezone: Europe/Jersey', 'Europe', 1.0, NULL),
('Europe/Kaliningrad', 'Europe/Kaliningrad', 'IANA timezone: Europe/Kaliningrad', 'Europe', 2.0, NULL),
('Europe/Kiev', 'Europe/Kiev', 'IANA timezone: Europe/Kiev', 'Europe', 3.0, NULL),
('Europe/Kirov', 'Europe/Kirov', 'IANA timezone: Europe/Kirov', 'Europe', 3.0, NULL),
('Europe/Kyiv', 'Europe/Kyiv', 'IANA timezone: Europe/Kyiv', 'Europe', 3.0, NULL),
('Europe/Lisbon', 'Europe/Lisbon', 'IANA timezone: Europe/Lisbon', 'Europe', 1.0, NULL),
('Europe/Ljubljana', 'Europe/Ljubljana', 'IANA timezone: Europe/Ljubljana', 'Europe', 2.0, NULL),
('Europe/London', 'Europe/London', 'IANA timezone: Europe/London', 'Europe', 1.0, NULL),
('Europe/Luxembourg', 'Europe/Luxembourg', 'IANA timezone: Europe/Luxembourg', 'Europe', 2.0, NULL),
('Europe/Madrid', 'Europe/Madrid', 'IANA timezone: Europe/Madrid', 'Europe', 2.0, NULL),
('Europe/Malta', 'Europe/Malta', 'IANA timezone: Europe/Malta', 'Europe', 2.0, NULL),
('Europe/Mariehamn', 'Europe/Mariehamn', 'IANA timezone: Europe/Mariehamn', 'Europe', 3.0, NULL),
('Europe/Minsk', 'Europe/Minsk', 'IANA timezone: Europe/Minsk', 'Europe', 3.0, NULL),
('Europe/Monaco', 'Europe/Monaco', 'IANA timezone: Europe/Monaco', 'Europe', 2.0, NULL),
('Europe/Moscow', 'Europe/Moscow', 'IANA timezone: Europe/Moscow', 'Europe', 3.0, NULL),
('Europe/Nicosia', 'Europe/Nicosia', 'IANA timezone: Europe/Nicosia', 'Europe', 3.0, NULL),
('Europe/Oslo', 'Europe/Oslo', 'IANA timezone: Europe/Oslo', 'Europe', 2.0, NULL),
('Europe/Paris', 'Europe/Paris', 'IANA timezone: Europe/Paris', 'Europe', 2.0, NULL),
('Europe/Podgorica', 'Europe/Podgorica', 'IANA timezone: Europe/Podgorica', 'Europe', 2.0, NULL),
('Europe/Prague', 'Europe/Prague', 'IANA timezone: Europe/Prague', 'Europe', 2.0, NULL),
('Europe/Riga', 'Europe/Riga', 'IANA timezone: Europe/Riga', 'Europe', 3.0, NULL),
('Europe/Rome', 'Europe/Rome', 'IANA timezone: Europe/Rome', 'Europe', 2.0, NULL),
('Europe/Samara', 'Europe/Samara', 'IANA timezone: Europe/Samara', 'Europe', 4.0, NULL),
('Europe/San_Marino', 'Europe/San Marino', 'IANA timezone: Europe/San_Marino', 'Europe', 2.0, NULL),
('Europe/Sarajevo', 'Europe/Sarajevo', 'IANA timezone: Europe/Sarajevo', 'Europe', 2.0, NULL),
('Europe/Saratov', 'Europe/Saratov', 'IANA timezone: Europe/Saratov', 'Europe', 4.0, NULL),
('Europe/Simferopol', 'Europe/Simferopol', 'IANA timezone: Europe/Simferopol', 'Europe', 3.0, NULL),
('Europe/Skopje', 'Europe/Skopje', 'IANA timezone: Europe/Skopje', 'Europe', 2.0, NULL),
('Europe/Sofia', 'Europe/Sofia', 'IANA timezone: Europe/Sofia', 'Europe', 3.0, NULL),
('Europe/Stockholm', 'Europe/Stockholm', 'IANA timezone: Europe/Stockholm', 'Europe', 2.0, NULL),
('Europe/Tallinn', 'Europe/Tallinn', 'IANA timezone: Europe/Tallinn', 'Europe', 3.0, NULL),
('Europe/Tirane', 'Europe/Tirane', 'IANA timezone: Europe/Tirane', 'Europe', 2.0, NULL),
('Europe/Tiraspol', 'Europe/Tiraspol', 'IANA timezone: Europe/Tiraspol', 'Europe', 3.0, NULL),
('Europe/Ulyanovsk', 'Europe/Ulyanovsk', 'IANA timezone: Europe/Ulyanovsk', 'Europe', 4.0, NULL),
('Europe/Uzhgorod', 'Europe/Uzhgorod', 'IANA timezone: Europe/Uzhgorod', 'Europe', 3.0, NULL),
('Europe/Vaduz', 'Europe/Vaduz', 'IANA timezone: Europe/Vaduz', 'Europe', 2.0, NULL),
('Europe/Vatican', 'Europe/Vatican', 'IANA timezone: Europe/Vatican', 'Europe', 2.0, NULL),
('Europe/Vienna', 'Europe/Vienna', 'IANA timezone: Europe/Vienna', 'Europe', 2.0, NULL),
('Europe/Vilnius', 'Europe/Vilnius', 'IANA timezone: Europe/Vilnius', 'Europe', 3.0, NULL),
('Europe/Volgograd', 'Europe/Volgograd', 'IANA timezone: Europe/Volgograd', 'Europe', 3.0, NULL),
('Europe/Warsaw', 'Europe/Warsaw', 'IANA timezone: Europe/Warsaw', 'Europe', 2.0, NULL),
('Europe/Zagreb', 'Europe/Zagreb', 'IANA timezone: Europe/Zagreb', 'Europe', 2.0, NULL),
('Europe/Zaporozhye', 'Europe/Zaporozhye', 'IANA timezone: Europe/Zaporozhye', 'Europe', 3.0, NULL),
('Europe/Zurich', 'Europe/Zurich', 'IANA timezone: Europe/Zurich', 'Europe', 2.0, NULL),
('Factory', 'Factory', 'IANA timezone: Factory', 'Other', 0.0, NULL),
('GB', 'GB', 'IANA timezone: GB', 'Other', 1.0, NULL),
('GB-Eire', 'GB-Eire', 'IANA timezone: GB-Eire', 'Other', 1.0, NULL),
('GMT', 'GMT', 'IANA timezone: GMT', 'Other', 0.0, NULL),
('GMT+0', 'GMT+0', 'IANA timezone: GMT+0', 'Other', 0.0, NULL),
('GMT-0', 'GMT-0', 'IANA timezone: GMT-0', 'Other', 0.0, NULL),
('GMT0', 'GMT0', 'IANA timezone: GMT0', 'Other', 0.0, NULL),
('Greenwich', 'Greenwich', 'IANA timezone: Greenwich', 'Other', 0.0, NULL),
('Hongkong', 'Hongkong', 'IANA timezone: Hongkong', 'Other', 8.0, NULL),
('HST', 'HST', 'IANA timezone: HST', 'Other', -10.0, NULL),
('Iceland', 'Iceland', 'IANA timezone: Iceland', 'Other', 0.0, NULL),
('Indian/Antananarivo', 'Indian/Antananarivo', 'IANA timezone: Indian/Antananarivo', 'Indian', 3.0, NULL),
('Indian/Chagos', 'Indian/Chagos', 'IANA timezone: Indian/Chagos', 'Indian', 6.0, NULL),
('Indian/Christmas', 'Indian/Christmas', 'IANA timezone: Indian/Christmas', 'Indian', 7.0, NULL),
('Indian/Cocos', 'Indian/Cocos', 'IANA timezone: Indian/Cocos', 'Indian', 6.5, NULL),
('Indian/Comoro', 'Indian/Comoro', 'IANA timezone: Indian/Comoro', 'Indian', 3.0, NULL),
('Indian/Kerguelen', 'Indian/Kerguelen', 'IANA timezone: Indian/Kerguelen', 'Indian', 5.0, NULL),
('Indian/Mahe', 'Indian/Mahe', 'IANA timezone: Indian/Mahe', 'Indian', 4.0, NULL),
('Indian/Maldives', 'Indian/Maldives', 'IANA timezone: Indian/Maldives', 'Indian', 5.0, NULL),
('Indian/Mauritius', 'Indian/Mauritius', 'IANA timezone: Indian/Mauritius', 'Indian', 4.0, NULL),
('Indian/Mayotte', 'Indian/Mayotte', 'IANA timezone: Indian/Mayotte', 'Indian', 3.0, NULL),
('Indian/Reunion', 'Indian/Reunion', 'IANA timezone: Indian/Reunion', 'Indian', 4.0, NULL),
('Iran', 'Iran', 'IANA timezone: Iran', 'Other', 3.5, NULL),
('Israel', 'Israel', 'IANA timezone: Israel', 'Other', 3.0, NULL),
('Jamaica', 'Jamaica', 'IANA timezone: Jamaica', 'Other', -5.0, NULL),
('Japan', 'Japan', 'IANA timezone: Japan', 'Other', 9.0, NULL),
('Kwajalein', 'Kwajalein', 'IANA timezone: Kwajalein', 'Other', 12.0, NULL),
('Libya', 'Libya', 'IANA timezone: Libya', 'Other', 2.0, NULL),
('MET', 'MET', 'IANA timezone: MET', 'Other', 2.0, NULL),
('Mexico/BajaNorte', 'Mexico/BajaNorte', 'IANA timezone: Mexico/BajaNorte', 'Mexico', -7.0, NULL),
('Mexico/BajaSur', 'Mexico/BajaSur', 'IANA timezone: Mexico/BajaSur', 'Mexico', -7.0, NULL),
('Mexico/General', 'Mexico/General', 'IANA timezone: Mexico/General', 'Mexico', -6.0, NULL),
('MST', 'MST', 'IANA timezone: MST', 'Other', -7.0, NULL),
('MST7MDT', 'MST7MDT', 'IANA timezone: MST7MDT', 'Other', -6.0, NULL),
('Navajo', 'Navajo', 'IANA timezone: Navajo', 'Other', -6.0, NULL),
('NZ', 'NZ', 'IANA timezone: NZ', 'Other', 13.0, NULL),
('NZ-CHAT', 'NZ-CHAT', 'IANA timezone: NZ-CHAT', 'Other', 13.75, NULL),
('Pacific/Apia', 'Pacific/Apia', 'IANA timezone: Pacific/Apia', 'Pacific', 13.0, NULL),
('Pacific/Auckland', 'Pacific/Auckland', 'IANA timezone: Pacific/Auckland', 'Pacific', 13.0, NULL),
('Pacific/Bougainville', 'Pacific/Bougainville', 'IANA timezone: Pacific/Bougainville', 'Pacific', 11.0, NULL),
('Pacific/Chatham', 'Pacific/Chatham', 'IANA timezone: Pacific/Chatham', 'Pacific', 13.75, NULL),
('Pacific/Chuuk', 'Pacific/Chuuk', 'IANA timezone: Pacific/Chuuk', 'Pacific', 10.0, NULL),
('Pacific/Easter', 'Pacific/Easter', 'IANA timezone: Pacific/Easter', 'Pacific', -5.0, NULL),
('Pacific/Efate', 'Pacific/Efate', 'IANA timezone: Pacific/Efate', 'Pacific', 11.0, NULL),
('Pacific/Enderbury', 'Pacific/Enderbury', 'IANA timezone: Pacific/Enderbury', 'Pacific', 13.0, NULL),
('Pacific/Fakaofo', 'Pacific/Fakaofo', 'IANA timezone: Pacific/Fakaofo', 'Pacific', 13.0, NULL),
('Pacific/Fiji', 'Pacific/Fiji', 'IANA timezone: Pacific/Fiji', 'Pacific', 12.0, NULL),
('Pacific/Funafuti', 'Pacific/Funafuti', 'IANA timezone: Pacific/Funafuti', 'Pacific', 12.0, NULL),
('Pacific/Galapagos', 'Pacific/Galapagos', 'IANA timezone: Pacific/Galapagos', 'Pacific', -6.0, NULL),
('Pacific/Gambier', 'Pacific/Gambier', 'IANA timezone: Pacific/Gambier', 'Pacific', -9.0, NULL),
('Pacific/Guadalcanal', 'Pacific/Guadalcanal', 'IANA timezone: Pacific/Guadalcanal', 'Pacific', 11.0, NULL),
('Pacific/Guam', 'Pacific/Guam', 'IANA timezone: Pacific/Guam', 'Pacific', 10.0, NULL),
('Pacific/Honolulu', 'Pacific/Honolulu', 'IANA timezone: Pacific/Honolulu', 'Pacific', -10.0, NULL),
('Pacific/Johnston', 'Pacific/Johnston', 'IANA timezone: Pacific/Johnston', 'Pacific', -10.0, NULL),
('Pacific/Kanton', 'Pacific/Kanton', 'IANA timezone: Pacific/Kanton', 'Pacific', 13.0, NULL),
('Pacific/Kiritimati', 'Pacific/Kiritimati', 'IANA timezone: Pacific/Kiritimati', 'Pacific', 14.0, NULL),
('Pacific/Kosrae', 'Pacific/Kosrae', 'IANA timezone: Pacific/Kosrae', 'Pacific', 11.0, NULL),
('Pacific/Kwajalein', 'Pacific/Kwajalein', 'IANA timezone: Pacific/Kwajalein', 'Pacific', 12.0, NULL),
('Pacific/Majuro', 'Pacific/Majuro', 'IANA timezone: Pacific/Majuro', 'Pacific', 12.0, NULL),
('Pacific/Marquesas', 'Pacific/Marquesas', 'IANA timezone: Pacific/Marquesas', 'Pacific', -9.5, NULL),
('Pacific/Midway', 'Pacific/Midway', 'IANA timezone: Pacific/Midway', 'Pacific', -11.0, NULL),
('Pacific/Nauru', 'Pacific/Nauru', 'IANA timezone: Pacific/Nauru', 'Pacific', 12.0, NULL),
('Pacific/Niue', 'Pacific/Niue', 'IANA timezone: Pacific/Niue', 'Pacific', -11.0, NULL),
('Pacific/Norfolk', 'Pacific/Norfolk', 'IANA timezone: Pacific/Norfolk', 'Pacific', 12.0, NULL),
('Pacific/Noumea', 'Pacific/Noumea', 'IANA timezone: Pacific/Noumea', 'Pacific', 11.0, NULL),
('Pacific/Pago_Pago', 'Pacific/Pago Pago', 'IANA timezone: Pacific/Pago_Pago', 'Pacific', -11.0, NULL),
('Pacific/Palau', 'Pacific/Palau', 'IANA timezone: Pacific/Palau', 'Pacific', 9.0, NULL),
('Pacific/Pitcairn', 'Pacific/Pitcairn', 'IANA timezone: Pacific/Pitcairn', 'Pacific', -8.0, NULL),
('Pacific/Pohnpei', 'Pacific/Pohnpei', 'IANA timezone: Pacific/Pohnpei', 'Pacific', 11.0, NULL),
('Pacific/Ponape', 'Pacific/Ponape', 'IANA timezone: Pacific/Ponape', 'Pacific', 11.0, NULL),
('Pacific/Port_Moresby', 'Pacific/Port Moresby', 'IANA timezone: Pacific/Port_Moresby', 'Pacific', 10.0, NULL),
('Pacific/Rarotonga', 'Pacific/Rarotonga', 'IANA timezone: Pacific/Rarotonga', 'Pacific', -10.0, NULL),
('Pacific/Saipan', 'Pacific/Saipan', 'IANA timezone: Pacific/Saipan', 'Pacific', 10.0, NULL),
('Pacific/Samoa', 'Pacific/Samoa', 'IANA timezone: Pacific/Samoa', 'Pacific', -11.0, NULL),
('Pacific/Tahiti', 'Pacific/Tahiti', 'IANA timezone: Pacific/Tahiti', 'Pacific', -10.0, NULL),
('Pacific/Tarawa', 'Pacific/Tarawa', 'IANA timezone: Pacific/Tarawa', 'Pacific', 12.0, NULL),
('Pacific/Tongatapu', 'Pacific/Tongatapu', 'IANA timezone: Pacific/Tongatapu', 'Pacific', 13.0, NULL),
('Pacific/Truk', 'Pacific/Truk', 'IANA timezone: Pacific/Truk', 'Pacific', 10.0, NULL),
('Pacific/Wake', 'Pacific/Wake', 'IANA timezone: Pacific/Wake', 'Pacific', 12.0, NULL),
('Pacific/Wallis', 'Pacific/Wallis', 'IANA timezone: Pacific/Wallis', 'Pacific', 12.0, NULL),
('Pacific/Yap', 'Pacific/Yap', 'IANA timezone: Pacific/Yap', 'Pacific', 10.0, NULL),
('Poland', 'Poland', 'IANA timezone: Poland', 'Other', 2.0, NULL),
('Portugal', 'Portugal', 'IANA timezone: Portugal', 'Other', 1.0, NULL),
('PRC', 'PRC', 'IANA timezone: PRC', 'Other', 8.0, NULL),
('PST8PDT', 'PST8PDT', 'IANA timezone: PST8PDT', 'Other', -7.0, NULL),
('ROC', 'ROC', 'IANA timezone: ROC', 'Other', 8.0, NULL),
('ROK', 'ROK', 'IANA timezone: ROK', 'Other', 9.0, NULL),
('Singapore', 'Singapore', 'IANA timezone: Singapore', 'Other', 8.0, NULL),
('Turkey', 'Turkey', 'IANA timezone: Turkey', 'Other', 3.0, NULL),
('UCT', 'UCT', 'IANA timezone: UCT', 'Other', 0.0, NULL),
('Universal', 'Universal', 'IANA timezone: Universal', 'Other', 0.0, NULL),
('US/Alaska', 'US/Alaska', 'IANA timezone: US/Alaska', 'US', -8.0, NULL),
('US/Aleutian', 'US/Aleutian', 'IANA timezone: US/Aleutian', 'US', -9.0, NULL),
('US/Arizona', 'US/Arizona', 'IANA timezone: US/Arizona', 'US', -7.0, NULL),
('US/Central', 'US/Central', 'IANA timezone: US/Central', 'US', -5.0, NULL),
('US/Eastern', 'US/Eastern', 'IANA timezone: US/Eastern', 'US', -4.0, NULL),
('US/East-Indiana', 'US/East-Indiana', 'IANA timezone: US/East-Indiana', 'US', -4.0, NULL),
('US/Hawaii', 'US/Hawaii', 'IANA timezone: US/Hawaii', 'US', -10.0, NULL),
('US/Indiana-Starke', 'US/Indiana-Starke', 'IANA timezone: US/Indiana-Starke', 'US', -5.0, NULL),
('US/Michigan', 'US/Michigan', 'IANA timezone: US/Michigan', 'US', -4.0, NULL),
('US/Mountain', 'US/Mountain', 'IANA timezone: US/Mountain', 'US', -6.0, NULL),
('US/Pacific', 'US/Pacific', 'IANA timezone: US/Pacific', 'US', -7.0, NULL),
('US/Samoa', 'US/Samoa', 'IANA timezone: US/Samoa', 'US', -11.0, NULL),
('UTC', 'UTC', 'IANA timezone: UTC', 'Other', 0.0, NULL),
('WET', 'WET', 'IANA timezone: WET', 'Other', 1.0, NULL),
('W-SU', 'W-SU', 'IANA timezone: W-SU', 'Other', 3.0, NULL),
('Zulu', 'Zulu', 'IANA timezone: Zulu', 'Other', 0.0, NULL);
-- Currencies
INSERT INTO meta.currencies (code, name, symbol, decimal_places, notes)
VALUES
('AED', 'UAE Dirham', 'د.إ', 2, NULL),
('AFN', 'Afghani', 'AFN', 2, NULL),
('ALL', 'Lek', 'ALL', 2, NULL),
('AMD', 'Armenian Dram', 'AMD', 2, NULL),
('AOA', 'Kwanza', 'AOA', 2, NULL),
('ARS', 'Argentine Peso', '$', 2, NULL),
('AUD', 'Australian Dollar', 'A$', 2, NULL),
('AWG', 'Aruban Florin', 'AWG', 2, NULL),
('AZN', 'Azerbaijan Manat', 'AZN', 2, NULL),
('BAM', 'Convertible Mark', 'BAM', 2, NULL),
('BBD', 'Barbados Dollar', 'BBD', 2, NULL),
('BDT', 'Taka', 'BDT', 2, NULL),
('BGN', 'Bulgarian Lev', 'BGN', 2, NULL),
('BHD', 'Bahraini Dinar', 'BHD', 3, NULL),
('BIF', 'Burundi Franc', 'BIF', 0, NULL),
('BMD', 'Bermudian Dollar', 'BMD', 2, NULL),
('BND', 'Brunei Dollar', 'BND', 2, NULL),
('BOB', 'Boliviano', 'BOB', 2, NULL),
('BOV', 'Mvdol', 'BOV', 2, NULL),
('BRL', 'Brazilian Real', 'R$', 2, NULL),
('BSD', 'Bahamian Dollar', 'BSD', 2, NULL),
('BTN', 'Ngultrum', 'BTN', 2, NULL),
('BWP', 'Pula', 'BWP', 2, NULL),
('BYN', 'Belarusian Ruble', 'BYN', 2, NULL),
('BZD', 'Belize Dollar', 'BZD', 2, NULL),
('CAD', 'Canadian Dollar', 'C$', 2, NULL),
('CDF', 'Congolese Franc', 'CDF', 2, NULL),
('CHE', 'WIR Euro', 'CHE', 2, NULL),
('CHF', 'Swiss Franc', 'CHF', 2, NULL),
('CHW', 'WIR Franc', 'CHW', 2, NULL),
('CLF', 'Unidad de Fomento', 'CLF', 4, NULL),
('CLP', 'Chilean Peso', '$', 0, NULL),
('CNY', 'Yuan Renminbi', '¥', 2, NULL),
('COP', 'Colombian Peso', 'COP', 2, NULL),
('COU', 'Unidad de Valor Real', 'COU', 2, NULL),
('CRC', 'Costa Rican Colon', 'CRC', 2, NULL),
('CUP', 'Cuban Peso', 'CUP', 2, NULL),
('CVE', 'Cabo Verde Escudo', 'CVE', 2, NULL),
('CZK', 'Czech Koruna', 'Kč', 2, NULL),
('DJF', 'Djibouti Franc', 'DJF', 0, NULL),
('DKK', 'Danish Krone', 'kr', 2, NULL),
('DOP', 'Dominican Peso', 'DOP', 2, NULL),
('DZD', 'Algerian Dinar', 'DZD', 2, NULL),
('EGP', 'Egyptian Pound', 'EGP', 2, NULL),
('ERN', 'Nakfa', 'ERN', 2, NULL),
('ETB', 'Ethiopian Birr', 'ETB', 2, NULL),
('EUR', 'Euro', '€', 2, NULL),
('FJD', 'Fiji Dollar', 'FJD', 2, NULL),
('FKP', 'Falkland Islands Pound', 'FKP', 2, NULL),
('GBP', 'Pound Sterling', '£', 2, NULL),
('GEL', 'Lari', 'GEL', 2, NULL),
('GHS', 'Ghana Cedi', 'GHS', 2, NULL),
('GIP', 'Gibraltar Pound', 'GIP', 2, NULL),
('GMD', 'Dalasi', 'GMD', 2, NULL),
('GNF', 'Guinean Franc', 'GNF', 0, NULL),
('GTQ', 'Quetzal', 'GTQ', 2, NULL),
('GYD', 'Guyana Dollar', 'GYD', 2, NULL),
('HKD', 'Hong Kong Dollar', 'HK$', 2, NULL),
('HNL', 'Lempira', 'HNL', 2, NULL),
('HTG', 'Gourde', 'HTG', 2, NULL),
('HUF', 'Forint', 'Ft', 2, NULL),
('IDR', 'Rupiah', 'Rp', 2, NULL),
('ILS', 'New Israeli Sheqel', '₪', 2, NULL),
('INR', 'Indian Rupee', '₹', 2, NULL),
('IQD', 'Iraqi Dinar', 'IQD', 3, NULL),
('IRR', 'Iranian Rial', 'IRR', 2, NULL),
('ISK', 'Iceland Krona', 'ISK', 0, NULL),
('JMD', 'Jamaican Dollar', 'JMD', 2, NULL),
('JOD', 'Jordanian Dinar', 'JOD', 3, NULL),
('JPY', 'Yen', '¥', 0, NULL),
('KES', 'Kenyan Shilling', 'KES', 2, NULL),
('KGS', 'Som', 'KGS', 2, NULL),
('KHR', 'Riel', 'KHR', 2, NULL),
('KMF', 'Comorian Franc', 'KMF', 0, NULL),
('KPW', 'North Korean Won', 'KPW', 2, NULL),
('KRW', 'Won', '₩', 0, NULL),
('KWD', 'Kuwaiti Dinar', 'KWD', 3, NULL),
('KYD', 'Cayman Islands Dollar', 'KYD', 2, NULL),
('KZT', 'Tenge', 'KZT', 2, NULL),
('LAK', 'Lao Kip', 'LAK', 2, NULL),
('LBP', 'Lebanese Pound', 'LBP', 2, NULL),
('LKR', 'Sri Lanka Rupee', 'LKR', 2, NULL),
('LRD', 'Liberian Dollar', 'LRD', 2, NULL),
('LSL', 'Loti', 'LSL', 2, NULL),
('LYD', 'Libyan Dinar', 'LYD', 3, NULL),
('MAD', 'Moroccan Dirham', 'MAD', 2, NULL),
('MDL', 'Moldovan Leu', 'MDL', 2, NULL),
('MGA', 'Malagasy Ariary', 'MGA', 2, NULL),
('MKD', 'Denar', 'MKD', 2, NULL),
('MMK', 'Kyat', 'MMK', 2, NULL),
('MNT', 'Tugrik', 'MNT', 2, NULL),
('MOP', 'Pataca', 'MOP', 2, NULL),
('MRU', 'Ouguiya', 'MRU', 2, NULL),
('MUR', 'Mauritius Rupee', 'MUR', 2, NULL),
('MVR', 'Rufiyaa', 'MVR', 2, NULL),
('MWK', 'Malawi Kwacha', 'MWK', 2, NULL),
('MXN', 'Mexican Peso', 'Mex$', 2, NULL),
('MXV', 'Mexican Unidad de Inversion (UDI)', 'MXV', 2, NULL),
('MYR', 'Malaysian Ringgit', 'RM', 2, NULL),
('MZN', 'Mozambique Metical', 'MZN', 2, NULL),
('NAD', 'Namibia Dollar', 'NAD', 2, NULL),
('NGN', 'Naira', 'NGN', 2, NULL),
('NIO', 'Cordoba Oro', 'NIO', 2, NULL),
('NOK', 'Norwegian Krone', 'kr', 2, NULL),
('NPR', 'Nepalese Rupee', 'NPR', 2, NULL),
('NZD', 'New Zealand Dollar', 'NZ$', 2, NULL),
('OMR', 'Rial Omani', 'OMR', 3, NULL),
('PAB', 'Balboa', 'PAB', 2, NULL),
('PEN', 'Sol', 'PEN', 2, NULL),
('PGK', 'Kina', 'PGK', 2, NULL),
('PHP', 'Philippine Peso', '₱', 2, NULL),
('PKR', 'Pakistan Rupee', 'PKR', 2, NULL),
('PLN', 'Zloty', 'zł', 2, NULL),
('PYG', 'Guarani', 'PYG', 0, NULL),
('QAR', 'Qatari Rial', 'QAR', 2, NULL),
('RON', 'Romanian Leu', 'RON', 2, NULL),
('RSD', 'Serbian Dinar', 'RSD', 2, NULL),
('RUB', 'Russian Ruble', '₽', 2, NULL),
('RWF', 'Rwanda Franc', 'RWF', 0, NULL),
('SAR', 'Saudi Riyal', '﷼', 2, NULL),
('SBD', 'Solomon Islands Dollar', 'SBD', 2, NULL),
('SCR', 'Seychelles Rupee', 'SCR', 2, NULL),
('SDG', 'Sudanese Pound', 'SDG', 2, NULL),
('SEK', 'Swedish Krona', 'kr', 2, NULL),
('SGD', 'Singapore Dollar', 'S$', 2, NULL),
('SHP', 'Saint Helena Pound', 'SHP', 2, NULL),
('SLE', 'Leone', 'SLE', 2, NULL),
('SOS', 'Somali Shilling', 'SOS', 2, NULL),
('SRD', 'Surinam Dollar', 'SRD', 2, NULL),
('SSP', 'South Sudanese Pound', 'SSP', 2, NULL),
('STN', 'Dobra', 'STN', 2, NULL),
('SVC', 'El Salvador Colon', 'SVC', 2, NULL),
('SYP', 'Syrian Pound', 'SYP', 2, NULL),
('SZL', 'Lilangeni', 'SZL', 2, NULL),
('THB', 'Baht', '฿', 2, NULL),
('TJS', 'Somoni', 'TJS', 2, NULL),
('TMT', 'Turkmenistan New Manat', 'TMT', 2, NULL),
('TND', 'Tunisian Dinar', 'TND', 3, NULL),
('TOP', 'Pa’anga', 'TOP', 2, NULL),
('TRY', 'Turkish Lira', '₺', 2, NULL),
('TTD', 'Trinidad and Tobago Dollar', 'TTD', 2, NULL),
('TWD', 'New Taiwan Dollar', 'NT$', 2, NULL),
('TZS', 'Tanzanian Shilling', 'TZS', 2, NULL),
('UAH', 'Hryvnia', 'UAH', 2, NULL),
('UGX', 'Uganda Shilling', 'UGX', 0, NULL),
('USD', 'US Dollar', '$', 2, NULL),
('USN', 'US Dollar (Next day)', 'USN', 2, NULL),
('UYI', 'Uruguay Peso en Unidades Indexadas (UI)', 'UYI', 0, NULL),
('UYU', 'Peso Uruguayo', 'UYU', 2, NULL),
('UYW', 'Unidad Previsional', 'UYW', 4, NULL),
('UZS', 'Uzbekistan Sum', 'UZS', 2, NULL),
('VED', 'Bolívar Soberano', 'VED', 2, NULL),
('VES', 'Bolívar Soberano', 'VES', 2, NULL),
('VND', 'Dong', '₫', 0, NULL),
('VUV', 'Vatu', 'VUV', 0, NULL),
('WST', 'Tala', 'WST', 2, NULL),
('XAD', 'Arab Accounting Dinar', 'XAD', 2, NULL),
('XAF', 'CFA Franc BEAC', 'XAF', 0, NULL),
('XAG', 'Silver', 'XAG', 2, NULL),
('XAU', 'Gold', 'XAU', 2, NULL),
('XBA', 'Bond Markets Unit European Composite Unit (EURCO)', 'XBA', 2, NULL),
('XBB', 'Bond Markets Unit European Monetary Unit (E.M.U.-6)', 'XBB', 2, NULL),
('XBC', 'Bond Markets Unit European Unit of Account 9 (E.U.A.-9)', 'XBC', 2, NULL),
('XBD', 'Bond Markets Unit European Unit of Account 17 (E.U.A.-17)', 'XBD', 2, NULL),
('XCD', 'East Caribbean Dollar', 'XCD', 2, NULL),
('XCG', 'Caribbean Guilder', 'XCG', 2, NULL),
('XDR', 'SDR (Special Drawing Right)', 'XDR', 2, NULL),
('XOF', 'CFA Franc BCEAO', 'XOF', 0, NULL),
('XPD', 'Palladium', 'XPD', 2, NULL),
('XPF', 'CFP Franc', 'XPF', 0, NULL),
('XPT', 'Platinum', 'XPT', 2, NULL),
('XSU', 'Sucre', 'XSU', 2, NULL),
('XTS', 'Codes specifically reserved for testing purposes', 'XTS', 2, NULL),
('XUA', 'ADB Unit of Account', 'XUA', 2, NULL),
('XXX', 'The codes assigned for transactions where no currency is involved', 'XXX', 2, NULL),
('YER', 'Yemeni Rial', 'YER', 2, NULL),
('ZAR', 'Rand', 'R', 2, NULL),
('ZMW', 'Zambian Kwacha', 'ZMW', 2, NULL),
('ZWG', 'Zimbabwe Gold', 'ZWG', 2, NULL);
-- Security Types (Interactive Brokers conventions)
INSERT INTO meta.security_types (code, name, description, notes)
VALUES
('ADR', 'American Depositary Receipt', 'Negotiable certificate representing shares in a foreign company', NULL),
('BOND', 'Bond', 'Government and corporate bonds', NULL),
('CASH', 'Forex', 'Foreign exchange currency pairs', NULL),
('CFD', 'CFD', 'Contracts for difference', NULL),
('CRYPTO', 'Cryptocurrency', 'Digital currencies and tokens', NULL),
('ETF', 'Exchange Traded Fund', 'Exchange-traded fund that tracks an index, commodity, or basket of assets', NULL),
('FOP', 'Futures Option', 'Options on futures contracts', NULL),
('FUND', 'Mutual Fund', 'Open and closed-end mutual funds', NULL),
('FUT', 'Future', 'Exchange-traded futures contracts', NULL),
('IOPT', 'Structured Product', 'Dutch warrants and structured products', NULL),
('OPT', 'Option', 'Equity and index options', NULL),
('STK', 'Stock', 'Common and preferred equities', NULL),
('WAR', 'Warrant', 'Equity warrants', NULL);

44
sql/08_script_log.sql Normal file
View file

@ -0,0 +1,44 @@
-- ============================================================================
-- Script Logging Table
-- ============================================================================
-- Persistent log for import scripts and library operations.
-- Provides queryable event log without cluttering CLI output.
--
-- Usage:
-- session = db.log_session("import_ohlc.py")
-- session.info("Starting import for AAPL")
-- session.warning("Missing data for 2024-01-15")
-- session.error("Failed to parse file", details={"file": "bad.csv"})
-- ============================================================================
-- Create script_log table in log schema
CREATE TABLE IF NOT EXISTS log.script_log (
id SERIAL PRIMARY KEY,
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
level VARCHAR(10) NOT NULL DEFAULT 'INFO' CHECK (level IN ('DEBUG', 'INFO', 'WARNING', 'ERROR')),
message TEXT NOT NULL,
script_name VARCHAR(100),
series_name VARCHAR(255),
session_id UUID,
details JSONB
);
-- Indexes for common query patterns
CREATE INDEX IF NOT EXISTS idx_script_log_timestamp ON log.script_log(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_script_log_session ON log.script_log(session_id) WHERE session_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_script_log_series ON log.script_log(series_name) WHERE series_name IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_script_log_level ON log.script_log(level);
-- View for recent logs (last 1000)
CREATE OR REPLACE VIEW log.script_log_recent AS
SELECT * FROM log.script_log
ORDER BY timestamp DESC
LIMIT 1000;
-- Table and column comments
COMMENT ON TABLE log.script_log IS 'Persistent log for import scripts and library operations';
COMMENT ON COLUMN log.script_log.level IS 'Log level: DEBUG, INFO, WARNING, ERROR';
COMMENT ON COLUMN log.script_log.session_id IS 'Groups logs from a single script run (UUID)';
COMMENT ON COLUMN log.script_log.script_name IS 'Name of the script or operation (e.g., import_ohlc.py)';
COMMENT ON COLUMN log.script_log.series_name IS 'Optional: link to specific series';
COMMENT ON COLUMN log.script_log.details IS 'Optional structured data stored as JSONB';

View file

@ -0,0 +1,48 @@
-- Migration: repair meta.drop_series_if_exists()
--
-- The function dropped a registered series without setting
-- application.dropping_series, so the prevent_direct_drop event trigger
-- rejected its own DROP:
--
-- Table series.X is registered in series and can only be dropped
-- using TimeseriesDB.drop_series()
--
-- Unlike meta.drop_series() and meta.drop_all_series(), which set the flag
-- correctly, this one never did -- so it always failed on exactly the case it
-- exists to handle. The Python API does not call it, which is why the defect
-- went unnoticed.
--
-- New databases pick this up from 05_procedures.sql. Run this against any
-- database initialised before the fix.
--
-- Safe to run more than once (CREATE OR REPLACE).
CREATE OR REPLACE FUNCTION meta.drop_series_if_exists(_table_name text, _schema_name text DEFAULT 'series'::text)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
-- Check if the series exists in series table
IF EXISTS (
SELECT 1 FROM meta.series
WHERE "table_name" = _table_name
AND "db_schema" = _schema_name
) THEN
-- Signal the prevent_direct_drop event trigger that this DROP is
-- coming from a sanctioned procedure. Without it the trigger rejects
-- the statement below, because the table is registered in meta.series.
-- Local to the transaction, so it cannot leak to later statements.
PERFORM set_config('application.dropping_series', 'true', true);
-- Drop the table
EXECUTE format('DROP TABLE IF EXISTS %I.%I', _schema_name, _table_name);
-- Remove from series table
DELETE FROM meta.series
WHERE "table_name" = _table_name
AND "db_schema" = _schema_name;
PERFORM set_config('application.dropping_series', 'false', true);
END IF;
END;
$function$;

View file

@ -0,0 +1,99 @@
-- Migration: Add Instrument Metadata Columns
-- Date: 2026-02-11
-- Description: Adds classification and trading specification columns to meta.exchanges and meta.instruments
-- ============================================================================
-- 1. Add columns to meta.exchanges
-- ============================================================================
-- Trading specifications
ALTER TABLE meta.exchanges ADD COLUMN IF NOT EXISTS default_tick_size DOUBLE PRECISION;
ALTER TABLE meta.exchanges ADD COLUMN IF NOT EXISTS default_tick_value DOUBLE PRECISION;
ALTER TABLE meta.exchanges ADD COLUMN IF NOT EXISTS default_extended_hours BOOLEAN DEFAULT false;
-- Geographic info
ALTER TABLE meta.exchanges ADD COLUMN IF NOT EXISTS country VARCHAR(3);
COMMENT ON COLUMN meta.exchanges.default_tick_size IS 'Default minimum price increment (e.g., 0.01 for stocks)';
COMMENT ON COLUMN meta.exchanges.default_tick_value IS 'Default value per tick in currency (0.01 for stocks = 1 cent per share)';
COMMENT ON COLUMN meta.exchanges.default_extended_hours IS 'Default for whether instruments support pre/post market trading';
COMMENT ON COLUMN meta.exchanges.country IS 'ISO 3166-1 alpha-3 country code (USA, GBR, JPN)';
-- ============================================================================
-- 2. Add columns to meta.instruments
-- ============================================================================
-- Basic info
ALTER TABLE meta.instruments ADD COLUMN IF NOT EXISTS description VARCHAR(255);
ALTER TABLE meta.instruments ADD COLUMN IF NOT EXISTS country VARCHAR(3);
-- Trading specifications
ALTER TABLE meta.instruments ADD COLUMN IF NOT EXISTS extended_hours BOOLEAN;
-- Company/Fund classification
ALTER TABLE meta.instruments ADD COLUMN IF NOT EXISTS sector VARCHAR(50);
ALTER TABLE meta.instruments ADD COLUMN IF NOT EXISTS industry VARCHAR(100);
ALTER TABLE meta.instruments ADD COLUMN IF NOT EXISTS category VARCHAR(50);
ALTER TABLE meta.instruments ADD COLUMN IF NOT EXISTS subcategory VARCHAR(50);
ALTER TABLE meta.instruments ADD COLUMN IF NOT EXISTS asset_class VARCHAR(30);
ALTER TABLE meta.instruments ADD COLUMN IF NOT EXISTS geography VARCHAR(30);
-- ETF-specific
ALTER TABLE meta.instruments ADD COLUMN IF NOT EXISTS underlying VARCHAR(30);
COMMENT ON COLUMN meta.instruments.description IS 'Full name/description of the instrument';
COMMENT ON COLUMN meta.instruments.country IS 'ISO 3166-1 alpha-3 country code (overrides exchange default)';
COMMENT ON COLUMN meta.instruments.extended_hours IS 'Override exchange default for pre/post market trading';
COMMENT ON COLUMN meta.instruments.sector IS 'GICS sector (e.g., Technology, Financials)';
COMMENT ON COLUMN meta.instruments.industry IS 'GICS industry (e.g., Software-Application)';
COMMENT ON COLUMN meta.instruments.category IS 'Custom categorization (e.g., Sector ETF, Broad Market)';
COMMENT ON COLUMN meta.instruments.subcategory IS 'Custom sub-categorization (e.g., S&P 500, Technology)';
COMMENT ON COLUMN meta.instruments.asset_class IS 'Asset class (Equity, Fixed Income, Commodity, Currency)';
COMMENT ON COLUMN meta.instruments.geography IS 'Geographic focus (US, International, Emerging)';
COMMENT ON COLUMN meta.instruments.underlying IS 'Underlying symbol for ETFs/derivatives (e.g., SPX, NDX)';
-- ============================================================================
-- 3. Update existing exchange data
-- ============================================================================
-- Update NYSE if it exists
UPDATE meta.exchanges
SET
default_tick_size = 0.01,
default_tick_value = 0.01,
default_extended_hours = true,
country = 'USA'
WHERE id = 'NYSE';
-- Update CME if it exists
UPDATE meta.exchanges
SET
country = 'USA',
default_extended_hours = true
WHERE id = 'CME';
-- Add NASDAQ if it doesn't exist
INSERT INTO meta.exchanges (
id, name, timezone, currency, default_sec_type,
default_tick_size, default_tick_value, default_extended_hours, country
)
SELECT 'NASDAQ', 'NASDAQ Stock Market', 'America/New_York', 'USD', 'STK',
0.01, 0.01, true, 'USA'
WHERE NOT EXISTS (SELECT 1 FROM meta.exchanges WHERE id = 'NASDAQ');
-- ============================================================================
-- Migration Complete
-- ============================================================================
-- Verify the changes
SELECT 'meta.exchanges columns:' as info;
SELECT column_name, data_type, character_maximum_length
FROM information_schema.columns
WHERE table_schema = 'meta' AND table_name = 'exchanges'
ORDER BY ordinal_position;
SELECT 'meta.instruments columns:' as info;
SELECT column_name, data_type, character_maximum_length
FROM information_schema.columns
WHERE table_schema = 'meta' AND table_name = 'instruments'
ORDER BY ordinal_position;

View file

@ -0,0 +1,111 @@
-- Migration: add identity constraints to the series registry and reference tables
--
-- meta.series had no primary key and no unique constraint, so two rows could
-- describe the same series -- and copy_series() actively created that, by
-- copying the source's table_id and db_schema. meta.currencies,
-- meta.security_types and meta.timezones had no key on their natural
-- identifier either, while add_currency()/add_security_type()/add_timezone()
-- all catch IntegrityError and promise an "already exists" error that no
-- constraint could raise.
--
-- These are small registries, not timeseries data. A btree key here costs
-- almost nothing, and this is unrelated to the deliberate decision NOT to
-- constrain series timestamps, where BRIN is used precisely because a btree
-- would be prohibitive at millions of rows per series.
--
-- New databases get these from 04_indexes.sql. Run this against any database
-- initialised before the fix.
--
-- Safe to run more than once: each constraint is added only if absent.
--
-- IMPORTANT: this ABORTS if duplicate rows already exist, printing what
-- collides. It does not guess which row to keep -- that is a data decision.
-- Resolve the duplicates, then run it again.
DO $$
DECLARE
dup_count integer;
dup_list text;
BEGIN
----------------------------------------------------------------------
-- Pre-flight: refuse to run if any natural key already collides.
----------------------------------------------------------------------
SELECT count(*), string_agg(format('%s.%s', db_schema, table_name), ', ')
INTO dup_count, dup_list
FROM (SELECT db_schema, table_name FROM meta.series
GROUP BY db_schema, table_name HAVING count(*) > 1) d;
IF dup_count > 0 THEN
RAISE EXCEPTION
'meta.series has % duplicated (db_schema, table_name): %. '
'Remove the extra rows, then re-run this migration.',
dup_count, dup_list;
END IF;
SELECT count(*), string_agg(table_id::text, ', ')
INTO dup_count, dup_list
FROM (SELECT table_id FROM meta.series
GROUP BY table_id HAVING count(*) > 1) d;
IF dup_count > 0 THEN
RAISE EXCEPTION
'meta.series has % duplicated table_id: %. Most likely created by '
'copy_series() before it was fixed. Reassign with '
'UPDATE meta.series SET table_id = nextval(''meta._table_meta__table_id_seq'') '
'WHERE ... , then re-run this migration.',
dup_count, dup_list;
END IF;
SELECT count(*), string_agg(code, ', ')
INTO dup_count, dup_list
FROM (SELECT code FROM meta.currencies GROUP BY code HAVING count(*) > 1) d;
IF dup_count > 0 THEN
RAISE EXCEPTION 'meta.currencies has % duplicated code: %', dup_count, dup_list;
END IF;
SELECT count(*), string_agg(code, ', ')
INTO dup_count, dup_list
FROM (SELECT code FROM meta.security_types GROUP BY code HAVING count(*) > 1) d;
IF dup_count > 0 THEN
RAISE EXCEPTION 'meta.security_types has % duplicated code: %', dup_count, dup_list;
END IF;
SELECT count(*), string_agg(iana_name, ', ')
INTO dup_count, dup_list
FROM (SELECT iana_name FROM meta.timezones GROUP BY iana_name HAVING count(*) > 1) d;
IF dup_count > 0 THEN
RAISE EXCEPTION 'meta.timezones has % duplicated iana_name: %', dup_count, dup_list;
END IF;
----------------------------------------------------------------------
-- Add the constraints, skipping any that already exist.
----------------------------------------------------------------------
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'series_pkey'
AND conrelid = 'meta.series'::regclass) THEN
ALTER TABLE meta.series ADD CONSTRAINT series_pkey PRIMARY KEY (table_id);
RAISE NOTICE 'added meta.series primary key on (table_id)';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'series_schema_table_key'
AND conrelid = 'meta.series'::regclass) THEN
ALTER TABLE meta.series ADD CONSTRAINT series_schema_table_key
UNIQUE (db_schema, table_name);
RAISE NOTICE 'added meta.series unique constraint on (db_schema, table_name)';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'currencies_pkey'
AND conrelid = 'meta.currencies'::regclass) THEN
ALTER TABLE meta.currencies ADD CONSTRAINT currencies_pkey PRIMARY KEY (code);
RAISE NOTICE 'added meta.currencies primary key on (code)';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'security_types_pkey'
AND conrelid = 'meta.security_types'::regclass) THEN
ALTER TABLE meta.security_types ADD CONSTRAINT security_types_pkey PRIMARY KEY (code);
RAISE NOTICE 'added meta.security_types primary key on (code)';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'timezones_pkey'
AND conrelid = 'meta.timezones'::regclass) THEN
ALTER TABLE meta.timezones ADD CONSTRAINT timezones_pkey PRIMARY KEY (iana_name);
RAISE NOTICE 'added meta.timezones primary key on (iana_name)';
END IF;
END $$;