Goal: table identifiers are common.QualifiedTable{Namespace, Table} structs everywhere
internally; the legacy dotted schema.table strings only exist at the API boundary and
in raw-table data. The compiler drives the conversion: fix every error by moving TO
structs, never by re-deriving dotted strings (except rules 5/6 below).
- NEVER split a string on "." to derive namespace+table, and NEVER concatenate
namespace+"."+table to build an identifier. Identifiers come from struct fields.
If a native source provides parts (e.g. binlog event schema+table, Mongo db+coll),
build
common.QualifiedTable{Namespace: ..., Table: ...}directly. - Proto TableMapping: use
.SourceTable/.DestinationTable(type *protos.QualifiedTable), convert withinternal.QualifiedTableFromProto(...)/internal.QualifiedTableProto(...). The legacy fieldsSourceTableIdentifier/DestinationTableIdentifierare EMPTY at runtime (cleared by boundary normalization) — never read them. - QRepConfig: use
.QualifiedWatermarkTableand.DestinationTable(legacy.WatermarkTable/.DestinationTableIdentifierare empty). TableSchema: use.Table(legacy.TableIdentifierempty). ChildTableRange:.ChildTable. SetupReplicationInput/CreateRawTableInput:.QualifiedTableMappings(legacy maps nil). EnsurePullabilityBatchInput:.SourceTables. SyncFlowOptions:.SrcTableIdMapping. RenameTableOption:.CurrentTable/.NewTable. - Go maps: keyed by
common.QualifiedTable(comparable). model.Record interface:GetDestinationTable()/GetSourceTable()return common.QualifiedTable; record structs haveSourceTable/DestinationTablefields. _peerdb_destination_table_nameraw-table values: ALWAYSqt.LegacyDotted()— both when writing (already done in connectors/utils/stream.go) and when building lookup maps in normalize code (map[string]...keyed byqt.LegacyDotted()is correct THERE ONLY, because the column holds rows from before this refactor too). Same for queue topic names defaulting from destination (kafka/pubsub: LegacyDotted).- Logs, error messages, metric/telemetry labels:
qt.String()(quoted form) unless the message interpolates into a context where the old dotted form is required. - SQL generation: quote per component — Postgres/Snowflake-style:
qt.String()uses QuoteIdentifier on both parts; MySQL:qt.MySQL(). For destinations with table-only names (ClickHouse, ES, queues): useqt.Tableand quote appropriately; namespace is "" there. BigQuery: pass the QualifiedTable toconvertToDatasetTable(handles project.dataset packed into Namespace and a legacy mid-flight{p, "d.t"}form by re-splitting Table at the first dot). - Snowflake: apply
SnowflakeIdentifierNormalizeper component, semantics for dotless identifiers must remain byte-identical to before. - Activity inputs that may arrive with only legacy strings (recorded by an old release)
are normalized via the matching
internal.Normalize*(...)call at the top of the activity (NOT inside connectors; connectors assume normalized inputs). Exception: connector methods receiving raw proto inputs directly as activity payloads (CreateRawTable, SetupReplication, EnsurePullability, RenameTables, RemoveTableEntriesFromRawTable) call the matching Normalize* defensively first. - Don't remove existing comments; match surrounding style; no comments that narrate the refactor (exception: a brief note where LegacyDotted is load-bearing, see existing examples in stream.go / pua/peerdb.go).
common.NormalizeTableIdentifier(s)exists for genuinely-legacy string inputs (catalog rows, raw-table values, Lua script inputs) — never for identifiers that are already structured.- After your changes:
cd /Users/ilia/Code/peerdb/flow && go build ./...must show no errors in YOUR packages (other packages may still fail — only fix files assigned to you). Rungofmt -won edited files. Keepgo vetclean for your packages.