mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-09-09 13:35:34 +00:00
changes channel storage to include the full key and a hashtag if isHashtag this prevents some collision issues and will improve message lookup accuracy as well as allow message search by hashtag channel, name or single byte hash in future
351 lines
11 KiB
SQL
351 lines
11 KiB
SQL
-- ============================================================
|
|
-- IATA CODES
|
|
-- ============================================================
|
|
|
|
-- name: UpsertIATA :exec
|
|
INSERT INTO iata_codes (iata)
|
|
VALUES ($1)
|
|
ON CONFLICT (iata) DO NOTHING;
|
|
|
|
-- name: GetIATA :one
|
|
SELECT * FROM iata_codes WHERE iata = $1;
|
|
|
|
-- name: ListIATAs :many
|
|
SELECT * FROM iata_codes ORDER BY iata;
|
|
|
|
-- name: UpsertIATADetails :exec
|
|
UPDATE iata_codes SET
|
|
display_name = $2,
|
|
approx_lat = $3,
|
|
approx_lng = $4
|
|
WHERE iata = $1;
|
|
|
|
-- ============================================================
|
|
-- OBSERVERS
|
|
-- ============================================================
|
|
|
|
-- name: UpsertObserver :one
|
|
INSERT INTO observers (public_key, last_seen)
|
|
VALUES ($1, NOW())
|
|
ON CONFLICT (public_key) DO UPDATE SET
|
|
last_seen = NOW(),
|
|
observation_count = observers.observation_count + 1
|
|
RETURNING *;
|
|
|
|
-- name: UpdateObserverStatus :one
|
|
UPDATE observers SET
|
|
display_name = COALESCE(NULLIF($2, ''), display_name),
|
|
observer_type = COALESCE(NULLIF($3, ''), observer_type),
|
|
software_version = COALESCE($4, software_version),
|
|
hardware_model = COALESCE($5, hardware_model),
|
|
firmware_version = COALESCE($6, firmware_version),
|
|
firmware_build = COALESCE($7, firmware_build),
|
|
radio_freq_mhz = COALESCE($8, radio_freq_mhz),
|
|
radio_sf = COALESCE($9, radio_sf),
|
|
radio_bw_khz = COALESCE($10, radio_bw_khz),
|
|
radio_cr = COALESCE($11, radio_cr),
|
|
battery_level = COALESCE($12, battery_level),
|
|
uptime_seconds = COALESCE($13, uptime_seconds),
|
|
status_metadata = $14,
|
|
last_status_at = NOW(),
|
|
last_seen = NOW()
|
|
WHERE public_key = $1
|
|
RETURNING id;
|
|
|
|
-- name: GetObserverByPubkey :one
|
|
SELECT * FROM observers WHERE public_key = $1;
|
|
|
|
-- name: ListObservers :many
|
|
SELECT * FROM observers ORDER BY last_seen DESC;
|
|
|
|
-- name: GetObserverLastIATA :one
|
|
SELECT iata FROM packet_observations
|
|
WHERE observer_id = $1
|
|
ORDER BY heard_at DESC
|
|
LIMIT 1;
|
|
|
|
-- name: GetObserverRadio :one
|
|
SELECT radio_freq_mhz, radio_bw_khz, radio_sf, radio_cr
|
|
FROM observers
|
|
WHERE id = $1;
|
|
|
|
-- ============================================================
|
|
-- OBSERVER BROKERS
|
|
-- ============================================================
|
|
|
|
-- name: UpsertObserverBroker :exec
|
|
INSERT INTO observer_brokers (observer_id, broker_name, last_seen, last_packet_at)
|
|
VALUES ($1, $2, NOW(), NOW())
|
|
ON CONFLICT (observer_id, broker_name) DO UPDATE SET
|
|
last_seen = NOW(),
|
|
last_packet_at = NOW();
|
|
|
|
-- ============================================================
|
|
-- PACKETS
|
|
-- ============================================================
|
|
|
|
-- name: UpsertPacket :one
|
|
INSERT INTO packets (
|
|
packet_hash,
|
|
payload_type,
|
|
payload_version,
|
|
route_type,
|
|
transport_codes_present,
|
|
region_code,
|
|
sub_region_code,
|
|
origin_pubkey,
|
|
raw_payload,
|
|
parsed_payload,
|
|
channel_hash,
|
|
first_heard_at,
|
|
last_heard_at,
|
|
observation_count
|
|
) VALUES (
|
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, NOW(), NOW(), 1
|
|
)
|
|
ON CONFLICT (packet_hash) DO UPDATE SET
|
|
last_heard_at = NOW(),
|
|
observation_count = packets.observation_count + 1
|
|
RETURNING *, (xmax = 0) AS inserted;
|
|
|
|
-- name: GetPacket :one
|
|
SELECT * FROM packets WHERE packet_hash = $1;
|
|
|
|
-- name: ListPackets :many
|
|
SELECT p.*
|
|
FROM packets p
|
|
WHERE
|
|
($1::smallint IS NULL OR p.payload_type = $1)
|
|
AND ($2::smallint IS NULL OR p.route_type = $2)
|
|
AND ($3::timestamptz IS NULL OR p.first_heard_at >= $3)
|
|
AND ($4::timestamptz IS NULL OR p.first_heard_at <= $4)
|
|
ORDER BY p.last_heard_at DESC
|
|
LIMIT $5;
|
|
|
|
-- name: ListPacketsAfterID :many
|
|
SELECT p.*
|
|
FROM packets p
|
|
JOIN packet_observations po ON po.packet_hash = p.packet_hash
|
|
WHERE po.id > $1
|
|
ORDER BY po.id ASC
|
|
LIMIT $2;
|
|
|
|
-- ============================================================
|
|
-- PACKET OBSERVATIONS
|
|
-- ============================================================
|
|
|
|
-- name: InsertObservation :one
|
|
INSERT INTO packet_observations (
|
|
packet_hash,
|
|
observer_id,
|
|
iata,
|
|
heard_at,
|
|
path_length_byte,
|
|
hash_size,
|
|
hop_count,
|
|
path_bytes,
|
|
rssi,
|
|
snr,
|
|
propagation_time_ms,
|
|
radio_freq_mhz,
|
|
spread_factor,
|
|
bandwidth_khz,
|
|
coding_rate,
|
|
source_broker
|
|
) VALUES (
|
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16
|
|
)
|
|
ON CONFLICT (packet_hash, observer_id, heard_at) DO NOTHING
|
|
RETURNING *;
|
|
|
|
-- name: ListObservationsForPacket :many
|
|
SELECT * FROM packet_observations
|
|
WHERE packet_hash = $1
|
|
ORDER BY heard_at ASC;
|
|
|
|
-- name: ListObservationsForObserver :many
|
|
SELECT * FROM packet_observations
|
|
WHERE observer_id = $1
|
|
AND ($2::timestamptz IS NULL OR heard_at >= $2)
|
|
ORDER BY heard_at DESC
|
|
LIMIT $3;
|
|
|
|
-- ============================================================
|
|
-- NODES
|
|
-- ============================================================
|
|
|
|
-- name: UpsertNode :one
|
|
INSERT INTO nodes (public_key, node_type, name, latitude, longitude, location_source, last_advert_at, last_seen)
|
|
VALUES ($1, $2, $3, $4, $5, 'advert', NOW(), NOW())
|
|
ON CONFLICT (public_key) DO UPDATE SET
|
|
node_type = EXCLUDED.node_type,
|
|
name = COALESCE(EXCLUDED.name, nodes.name),
|
|
latitude = COALESCE(EXCLUDED.latitude, nodes.latitude),
|
|
longitude = COALESCE(EXCLUDED.longitude, nodes.longitude),
|
|
location_source = CASE WHEN EXCLUDED.latitude IS NOT NULL THEN 'advert' ELSE nodes.location_source END,
|
|
last_advert_at = NOW(),
|
|
last_seen = NOW()
|
|
RETURNING *;
|
|
|
|
-- name: SetNodeMultibytePaths :exec
|
|
UPDATE nodes SET supports_multibyte_paths = TRUE
|
|
WHERE id = $1 AND supports_multibyte_paths = FALSE;
|
|
|
|
-- name: SetNodeMultibyteTraces :exec
|
|
UPDATE nodes SET supports_multibyte_traces = TRUE
|
|
WHERE id = $1 AND supports_multibyte_traces = FALSE;
|
|
|
|
-- name: GetNodeByPubkey :one
|
|
SELECT * FROM nodes WHERE public_key = $1;
|
|
|
|
-- name: ListNodes :many
|
|
SELECT * FROM nodes
|
|
WHERE
|
|
($1::smallint IS NULL OR node_type = $1)
|
|
ORDER BY last_seen DESC
|
|
LIMIT $2;
|
|
|
|
-- ============================================================
|
|
-- NODE IATAS
|
|
-- ============================================================
|
|
|
|
-- name: UpsertNodeIATA :exec
|
|
INSERT INTO node_iatas (node_id, iata, last_heard, observation_count)
|
|
VALUES ($1, $2, NOW(), 1)
|
|
ON CONFLICT (node_id, iata) DO UPDATE SET
|
|
last_heard = NOW(),
|
|
observation_count = node_iatas.observation_count + 1;
|
|
|
|
-- name: UpsertNodeShortID :exec
|
|
INSERT INTO node_short_ids (node_id, iata, prefix_4)
|
|
VALUES ($1, $2, $3)
|
|
ON CONFLICT (node_id, iata) DO NOTHING;
|
|
|
|
-- ============================================================
|
|
-- CHANNELS
|
|
-- ============================================================
|
|
|
|
-- name: UpsertChannel :one
|
|
-- Upsert a channel by (hash, key_fingerprint). Pass NULL fingerprint for
|
|
-- hash-only records (key unknown). Returns the channel row.
|
|
INSERT INTO channels (channel_hash, key_fingerprint, name, hashtag, is_hashtag, key_known, last_seen)
|
|
VALUES ($1, $2::bytea, $3, $4, $5, ($2 IS NOT NULL), NOW())
|
|
ON CONFLICT (channel_hash, key_fingerprint) DO UPDATE SET
|
|
last_seen = NOW(),
|
|
name = COALESCE(EXCLUDED.name, channels.name),
|
|
message_count = CASE WHEN $6 THEN channels.message_count + 1 ELSE channels.message_count END
|
|
RETURNING *;
|
|
|
|
-- name: SetChannelKeyKnown :exec
|
|
UPDATE channels SET key_known = TRUE
|
|
WHERE channel_hash = $1 AND key_fingerprint = $2;
|
|
|
|
-- name: ListChannels :many
|
|
SELECT * FROM channels ORDER BY last_seen DESC LIMIT $1;
|
|
|
|
-- name: GetChannelsByHash :many
|
|
-- Returns all channels for a given hash (may be multiple on hash collision).
|
|
SELECT * FROM channels WHERE channel_hash = $1 ORDER BY last_seen DESC;
|
|
|
|
-- name: GetChannelByHashAndFingerprint :one
|
|
SELECT * FROM channels WHERE channel_hash = $1 AND key_fingerprint = $2;
|
|
|
|
-- name: GetChannelByHashtag :one
|
|
SELECT * FROM channels WHERE hashtag = $1;
|
|
|
|
-- ============================================================
|
|
-- CHANNEL MESSAGES
|
|
-- ============================================================
|
|
|
|
-- name: InsertChannelMessage :exec
|
|
INSERT INTO channel_messages (channel_id, packet_hash, sender_name, sender_pubkey, content, sent_at)
|
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
ON CONFLICT (packet_hash) DO NOTHING;
|
|
|
|
-- name: ListChannelMessages :many
|
|
SELECT * FROM channel_messages
|
|
WHERE channel_id = $1
|
|
AND ($2::timestamptz IS NULL OR sent_at >= $2)
|
|
ORDER BY sent_at DESC
|
|
LIMIT $3;
|
|
|
|
-- ============================================================
|
|
-- STATS
|
|
-- ============================================================
|
|
|
|
-- name: GetStatsOverview :one
|
|
SELECT
|
|
COUNT(DISTINCT po.packet_hash) AS total_packets,
|
|
COUNT(*) AS total_observations,
|
|
COUNT(DISTINCT po.observer_id) AS active_observers,
|
|
COUNT(DISTINCT po.iata) AS active_iatas
|
|
FROM packet_observations po
|
|
WHERE po.heard_at > NOW() - INTERVAL '24 hours'
|
|
AND ($1::char(3) IS NULL OR po.iata = $1);
|
|
|
|
-- name: GetHourlyStats :many
|
|
SELECT * FROM mv_hourly_iata_stats
|
|
WHERE ($1::char(3) IS NULL OR iata = $1)
|
|
AND hour >= NOW() - $2::interval
|
|
ORDER BY iata, hour;
|
|
|
|
-- name: GetTopNodes :many
|
|
SELECT * FROM mv_top_nodes_by_iata
|
|
WHERE ($1::char(3) IS NULL OR iata = $1)
|
|
ORDER BY observation_count DESC
|
|
LIMIT $2;
|
|
|
|
-- ============================================================
|
|
-- REGIONS
|
|
-- ============================================================
|
|
|
|
-- name: ListRegions :many
|
|
SELECT id, slug, name
|
|
FROM regions
|
|
ORDER BY display_order, name;
|
|
|
|
-- name: GetRegion :one
|
|
SELECT id, slug, name, description, center_lat, center_lng, zoom_level
|
|
FROM regions
|
|
WHERE id = $1;
|
|
|
|
-- name: GetRegionIATAs :many
|
|
SELECT iata FROM region_iatas
|
|
WHERE region_id = $1
|
|
ORDER BY iata;
|
|
|
|
-- name: UpsertRegion :one
|
|
INSERT INTO regions (slug, name, description, display_order, center_lat, center_lng, zoom_level, updated_at)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
|
|
ON CONFLICT (slug) DO UPDATE SET
|
|
name = EXCLUDED.name,
|
|
description = EXCLUDED.description,
|
|
display_order = EXCLUDED.display_order,
|
|
center_lat = EXCLUDED.center_lat,
|
|
center_lng = EXCLUDED.center_lng,
|
|
zoom_level = EXCLUDED.zoom_level,
|
|
updated_at = NOW()
|
|
RETURNING id;
|
|
|
|
-- name: UpsertRegionIATA :exec
|
|
INSERT INTO region_iatas (region_id, iata)
|
|
VALUES ($1, $2)
|
|
ON CONFLICT (region_id, iata) DO NOTHING;
|
|
|
|
-- ============================================================
|
|
-- HELPERS
|
|
-- ============================================================
|
|
|
|
-- name: ResolvePathHashes :many
|
|
SELECT DISTINCT n.id
|
|
FROM node_short_ids ns
|
|
JOIN nodes n ON n.id = ns.node_id
|
|
WHERE ns.iata = $1
|
|
AND CASE
|
|
WHEN cardinality($2::bytea[]) > 0 AND length($2[1]) = 1 THEN ns.prefix_1 = ANY($2)
|
|
WHEN cardinality($2::bytea[]) > 0 AND length($2[1]) = 2 THEN ns.prefix_2 = ANY($2)
|
|
WHEN cardinality($2::bytea[]) > 0 AND length($2[1]) = 3 THEN ns.prefix_3 = ANY($2)
|
|
WHEN cardinality($2::bytea[]) > 0 AND length($2[1]) = 4 THEN ns.prefix_4 = ANY($2)
|
|
ELSE FALSE
|
|
END;
|