From 91a4c46b3cd21af4411859ff7c0a6017a72a295c Mon Sep 17 00:00:00 2001 From: Mark Chadwick Date: Sat, 23 Nov 2024 17:34:08 +1300 Subject: [PATCH] update --- cmd/deltadb/delta.svg | 3108 ++++++++++++++++++++++------------------ meta/sqlite/db.go | 68 +- meta/sqlite/gauge.go | 101 ++ meta/sqlite/station.go | 58 + meta/sqlite/table.go | 120 ++ 5 files changed, 2048 insertions(+), 1407 deletions(-) create mode 100644 meta/sqlite/gauge.go create mode 100644 meta/sqlite/table.go diff --git a/cmd/deltadb/delta.svg b/cmd/deltadb/delta.svg index fc306b246..996f0a1b9 100644 --- a/cmd/deltadb/delta.svg +++ b/cmd/deltadb/delta.svg @@ -4,1538 +4,1900 @@ - - + + structs - + asset - -asset - -🔑 - -asset_id - - - -* - -  - -model_id - - - -* - -  - -serial - - - -* - -  - -number - - - -* - -  - -notes - - - -* + +asset + +🔑 + +asset_id + + + +* + +  + +model_id + + + +* + +  + +serial + + + +* + +  + +number + + + +* + +  + +notes + + + +* - + model - -model - -🔑 - -model_id - - - -* - -  - -make_id - - - -* - -  - -model - - - -* + +model + +🔑 + +model_id + + + +* + +  + +make_id + + + +* + +  + +model + + + +* - + asset:e->model:w - - + + bedrock - -bedrock - -🔑 - -bedrock_id - - - -* - -  - -bedrock - - - -* + +bedrock + +🔑 + +bedrock_id + + + +* + +  + +bedrock + + + +* calibration - -calibration - -🔑 - -calibration_id - - - -* - -  - -asset_id - - - -* - -  - -number - - - -* - -  - -scale_factor - - - -* - -  - -scale_bias - - - -* - -  - -scale_absolute - - - -* - -  - -frequency - - - - -  - -start_date - - - -* - -  - -end_date - - - -* + +calibration + +🔑 + +calibration_id + + + +* + +  + +asset_id + + + +* + +  + +number + + + +* + +  + +scale_factor + + + +* + +  + +scale_bias + + + +* + +  + +scale_absolute + + + +* + +  + +frequency + + + + +  + +start_date + + + +* + +  + +end_date + + + +* - + calibration:e->asset:w - - + + channel - -channel - -🔑 - -channel_id - - - -* - -  - -model_id - - - -* - -  - -channel_type - - - -* - -  - -number - - - -* - -  - -sampling_rate - - - -* - -  - -response - - - -* + +channel + +🔑 + +channel_id + + + +* + +  + +model_id + + + +* + +  + +channel_type + + + +* + +  + +number + + + +* + +  + +sampling_rate + + + +* + +  + +response + + + +* - + channel:e->model:w - - + + citation - -citation - -🔑 - -citation_id - - - -* - -  - -key - - - -* - -  - -author - - - -* - -  - -year - - - -* - -  - -title - - - -* - -  - -published - - - - -  - -volume - - - - -  - -pages - - - - -  - -doi - - - - -  - -link - - - - -  - -retrieved - - - + +citation + +🔑 + +citation_id + + + +* + +  + +key + + + +* + +  + +author + + + +* + +  + +year + + + +* + +  + +title + + + +* + +  + +published + + + + +  + +volume + + + + +  + +pages + + + + +  + +doi + + + + +  + +link + + + + +  + +retrieved + + + - + +class + +class + +🔑 + +class_id + + + +* + +  + +station_id + + + +* + +  + +site_class + + + +* + +  + +vs30 + + + +* + +  + +vs30_quality + + + +* + +  + +tsite + + + +* + +  + +tsite_method + + + +* + +  + +tsite_quality + + + +* + +  + +basement_depth + + + +* + +  + +depth_quality + + + +* + +  + +link + + + + +  + +notes + + + + + + +station + +station + +🔑 + +station_id + + + +* + +  + +datum_id + + + +* + +  + +station + + + +* + +  + +name + + + +* + +  + +latitude + + + +* + +  + +longitude + + + +* + +  + +elevation + + + +* + +  + +depth + + + +* + +  + +start_date + + + +* + +  + +end_date + + + +* + + + +class:e->station:w + + + + + +class_citation + +class_citation + +🔑 + +class_citation_id + + + +* + +  + +class_id + + + +* + +  + +citation_id + + + +* + + + +class_citation:e->citation:w + + + + + +class_citation:e->class:w + + + + + component - -component - -🔑 - -component_id - - - -* - -  - -model_id - - - -* - -  - -component_type - - - - -  - -number - - - -* - -  - -source - - - - -  - -subsource - - - -* - -  - -dip - - - -* - -  - -azimuth - - - -* - -  - -types - - - -* - -  - -sampling_rate - - - - -  - -response - - - -* + +component + +🔑 + +component_id + + + +* + +  + +model_id + + + +* + +  + +component_type + + + + +  + +number + + + +* + +  + +source + + + + +  + +subsource + + + +* + +  + +dip + + + +* + +  + +azimuth + + + +* + +  + +types + + + +* + +  + +sampling_rate + + + + +  + +response + + + +* component:e->model:w - - + + + + + +constituent + +constituent + +🔑 + +constituent_id + + + +* + +  + +gauge_id + + + +* + +  + +number + + + +* + +  + +constituent + + + +* + +  + +amplitude + + + +* + +  + +lag + + + +* + +  + +start_date + + + +* + +  + +end_date + + + +* + + + +gauge + +gauge + +🔑 + +gauge_id + + + +* + +  + +gauge + + + +* + +  + +identification_number + + + +* + +  + +analysis_time_zone + + + +* + +  + +analysis_latitude + + + +* + +  + +analysis_longitude + + + +* + +  + +crex_tag + + + +* + +  + +start_date + + + +* + +  + +end_date + + + +* + + + +constituent:e->gauge:w + + + + + +dart + +dart + +🔑 + +dart_id + + + +* + +  + +station_id + + + +* + +  + +pid + + + +* + +  + +wmo_identifier + + + +* + +  + +start_date + + + +* + +  + +end_date + + + +* + + + +dart:e->station:w + + - + datum - -datum - -🔑 - -datum_id - - - -* - -  - -datum - - - -* + +datum + +🔑 + +datum_id + + + +* + +  + +datum + + + +* - + feature - -feature - -🔑 - -feature_id - - - -* - -  - -site_id - - - -* - -  - -sublocation - - - - -  - -property - - - -* - -  - -description - - - - -  - -aspect - - - - -  - -start_date - - - -* - -  - -end_date - - - -* + +feature + +🔑 + +feature_id + + + +* + +  + +site_id + + + +* + +  + +sublocation + + + + +  + +property + + + +* + +  + +description + + + + +  + +aspect + + + + +  + +start_date + + + +* + +  + +end_date + + + +* - + site - -site - -🔑 - -site_id - - - -* - -  - -station_id - - - -* - -  - -datum_id - - - -* - -  - -location - - - -* - -  - -latitude - - - -* - -  - -longitude - - - -* - -  - -elevation - - - -* - -  - -depth - - - -* - -  - -survey - - - -* - -  - -start_date - - - -* - -  - -end_date - - - -* + +site + +🔑 + +site_id + + + +* + +  + +station_id + + + +* + +  + +datum_id + + + +* + +  + +location + + + +* + +  + +latitude + + + +* + +  + +longitude + + + +* + +  + +elevation + + + +* + +  + +depth + + + +* + +  + +survey + + + +* + +  + +start_date + + + +* + +  + +end_date + + + +* feature:e->site:w - - + + - + firmware - -firmware - -🔑 - -firmware_id - - - -* - -  - -asset_id - - - -* - -  - -version - - - -* - -  - -notes - - - -* - -  - -start_date - - - -* - -  - -end_date - - - -* + +firmware + +🔑 + +firmware_id + + + +* + +  + +asset_id + + + +* + +  + +version + + + +* + +  + +notes + + + +* + +  + +start_date + + + +* + +  + +end_date + + + +* firmware:e->asset:w - - + + - + foundation_type - -foundation_type - -🔑 - -foundation_type_id - - - -* - -  - -foundation_type - - - -* + +foundation_type + +🔑 + +foundation_type_id + + + +* + +  + +foundation_type + + + +* - + geology - -geology - -🔑 - -geology_id - - - -* - -  - -geology - - - -* + +geology + +🔑 + +geology_id + + + +* + +  + +geology + + + +* - + make - -make - -🔑 - -make_id - - - -* - -  - -make - - - -* + +make + +🔑 + +make_id + + + +* + +  + +make + + + +* - + mark - -mark - -🔑 - -mark_id - - - -* - -  - -datum_id - - - -* - -  - -mark - - - -* - -  - -igs - - - -* - -  - -name - - - -* - -  - -latitude - - - -* - -  - -longitude - - - -* - -  - -elevation - - - -* - -  - -start_date - - -* - -  - -end_date - - -* + +mark + +🔑 + +mark_id + + + +* + +  + +datum_id + + + +* + +  + +mark + + + +* + +  + +igs + + + +* + +  + +name + + + +* + +  + +latitude + + + +* + +  + +longitude + + + +* + +  + +elevation + + + +* + +  + +start_date + + +* + +  + +end_date + + +* - + mark:e->datum:w - - + + - + mark_network - -mark_network - -🔑 - -mark_network_id - - - -* - -  - -mark_id - - - -* - -  - -network_id - - - -* + +mark_network + +🔑 + +mark_network_id + + + +* + +  + +mark_id + + + +* + +  + +network_id + + + +* - + mark_network:e->mark:w - - + + - + network - -network - -🔑 - -network_id - - - -* - -  - -network - - - -* - -  - -external - - - -* - -  - -description - - - -* - -  - -restricted - - - -* + +network + +🔑 + +network_id + + + +* + +  + +network + + + +* + +  + +external + + + +* + +  + +description + + + +* + +  + +restricted + + + +* - + mark_network:e->network:w - - + + - + mark_type - -mark_type - -🔑 - -mark_type_id - - - -* - -  - -mark_type - - - -* + +mark_type + +🔑 + +mark_type_id + + + +* + +  + +mark_type + + + +* - + method - -method - -🔑 - -method_id - - - -* - -  - -method - - - -* + +method + +🔑 + +method_id + + + +* + +  + +method + + + +* - + model:e->make:w - - + + - + monument - -monument - -🔑 - -monument_id - - - -* - -  - -mark_id - - - -* - -  - -mark_type_id - - - -* - -  - -monument_type_id - - - -* - -  - -foundation_type_id - - - -* - -  - -bedrock_id - - - -* - -  - -geology_id - - - -* - -  - -domes_number - - - -* - -  - -ground_relationship - - - -* - -  - -foundation_depth - - - -* - -  - -start_date - - -* - -  - -end_date - - -* + +monument + +🔑 + +monument_id + + + +* + +  + +mark_id + + + +* + +  + +mark_type_id + + + +* + +  + +monument_type_id + + + +* + +  + +foundation_type_id + + + +* + +  + +bedrock_id + + + +* + +  + +geology_id + + + +* + +  + +domes_number + + + +* + +  + +ground_relationship + + + +* + +  + +foundation_depth + + + +* + +  + +start_date + + +* + +  + +end_date + + +* - + monument:e->bedrock:w - - + + - + monument:e->foundation_type:w - - + + - + monument:e->geology:w - - + + - + monument:e->mark:w - - + + - + monument:e->mark_type:w - - + + - + monument_type - -monument_type - -🔑 - -monument_type_id - - - -* - -  - -monument_type - - - -* + +monument_type + +🔑 + +monument_type_id + + + +* + +  + +monument_type + + + +* - + monument:e->monument_type:w - - + + - + point - -point - -🔑 - -point_id - - - -* - -  - -sample_id - - - -* - -  - -datum_id - - - -* - -  - -location - - - -* - -  - -latitude - - - -* - -  - -longitude - - - -* - -  - -elevation - - - -* - -  - -depth - - - -* - -  - -survey - - - -* - -  - -start_date - - - -* - -  - -end_date - - - -* + +point + +🔑 + +point_id + + + +* + +  + +sample_id + + + +* + +  + +datum_id + + + +* + +  + +location + + + +* + +  + +latitude + + + +* + +  + +longitude + + + +* + +  + +elevation + + + +* + +  + +depth + + + +* + +  + +survey + + + +* + +  + +start_date + + + +* + +  + +end_date + + + +* - + point:e->datum:w - - + + - + sample - -sample - -🔑 - -sample_id - - - -* - -  - -datum_id - - - -* - -  - -station - - - -* - -  - -name - - - -* - -  - -latitude - - - -* - -  - -longitude - - - -* - -  - -elevation - - - -* - -  - -depth - - - - -  - -start_date - - - -* - -  - -end_date - - - -* + +sample + +🔑 + +sample_id + + + +* + +  + +datum_id + + + +* + +  + +station + + + +* + +  + +name + + + +* + +  + +latitude + + + +* + +  + +longitude + + + +* + +  + +elevation + + + +* + +  + +depth + + + + +  + +start_date + + + +* + +  + +end_date + + + +* - + point:e->sample:w - - + + - + sample:e->datum:w - - + + - + site:e->datum:w - - - - - -station - -station - -🔑 - -station_id - - - -* - -  - -datum_id - - - -* - -  - -station - - - -* - -  - -name - - - -* - -  - -latitude - - - -* - -  - -longitude - - - -* - -  - -elevation - - - -* - -  - -depth - - - -* - -  - -start_date - - - -* - -  - -end_date - - - -* + + - + site:e->station:w - - + + - + station:e->datum:w - - + + - + station_network - -station_network - -🔑 - -station_network_id - - - -* - -  - -station_id - - - -* - -  - -network_id - - - -* + +station_network + +🔑 + +station_network_id + + + +* + +  + +station_id + + + +* + +  + +network_id + + + +* - + station_network:e->network:w - - + + - + station_network:e->station:w - - + + - + visibility - -visibility - -🔑 - -visibility_id - - - -* - -  - -mark_id - - - -* - -  - -sky_visibility - - - -* - -  - -start_date - - - -* - -  - -end_date - - - -* + +visibility + +🔑 + +visibility_id + + + +* + +  + +mark_id + + + +* + +  + +sky_visibility + + + +* + +  + +start_date + + + +* + +  + +end_date + + + +* visibility:e->mark:w - - + + diff --git a/meta/sqlite/db.go b/meta/sqlite/db.go index 663a18186..a1f95374e 100644 --- a/meta/sqlite/db.go +++ b/meta/sqlite/db.go @@ -201,6 +201,19 @@ func (d DB) Init(ctx context.Context, list []meta.TableList) error { if err := d.prepare(ctx, tx, feature.Insert(), feature.Columns(l)...); err != nil { return err } + case "Class": + if err := d.exec(ctx, tx, class.Create); err != nil { + return err + } + if err := d.prepare(ctx, tx, class.Insert(), class.Columns(l)...); err != nil { + return err + } + if err := d.exec(ctx, tx, classCitation.Create); err != nil { + return err + } + if err := d.prepare(ctx, tx, classCitation.Insert(), class.Links(l)...); err != nil { + return err + } case "Mark": if err := d.exec(ctx, tx, mark.Create); err != nil { return err @@ -258,6 +271,27 @@ func (d DB) Init(ctx context.Context, list []meta.TableList) error { if err := d.prepare(ctx, tx, visibility.Insert(), visibility.Columns(l)...); err != nil { return err } + case "Gauge": + if err := d.exec(ctx, tx, gauge.Create); err != nil { + return err + } + if err := d.prepare(ctx, tx, gauge.Insert(), gauge.Columns(l)...); err != nil { + return err + } + case "Constituent": + if err := d.exec(ctx, tx, constituent.Create); err != nil { + return err + } + if err := d.prepare(ctx, tx, constituent.Insert(), constituent.Columns(l)...); err != nil { + return err + } + case "Dart": + if err := d.exec(ctx, tx, dartCreate); err != nil { + return err + } + if err := d.prepare(ctx, tx, dart.Insert(), dart.Columns(l)...); err != nil { + return err + } /* case "Datalogger": if err := d.prepare(ctx, tx, placeRoleInsert(), columns(l, placeRoleFields(), nil, "")...); err != nil { @@ -416,40 +450,6 @@ func (d DB) Init(ctx context.Context, list []meta.TableList) error { if err := d.prepare(ctx, tx, connectionInsert(), columns(l, connectionFields(), nil, "")...); err != nil { return err } - case "Gauge": - if err := d.exec(ctx, tx, gaugeCreate); err != nil { - return err - } - if err := d.prepare(ctx, tx, gaugeInsert(), columns(l, gaugeFields(), nil, "")...); err != nil { - return err - } - case "Constituent": - if err := d.exec(ctx, tx, constituentCreate); err != nil { - return err - } - if err := d.prepare(ctx, tx, constituentInsert(), columns(l, constituentFields(), nil, "")...); err != nil { - return err - } - case "Dart": - if err := d.exec(ctx, tx, dartCreate); err != nil { - return err - } - if err := d.prepare(ctx, tx, dartInsert(), columns(l, dartFields(), nil, "")...); err != nil { - return err - } - case "Class": - if err := d.exec(ctx, tx, classCreate); err != nil { - return err - } - if err := d.prepare(ctx, tx, classInsert(), columns(l, classFields(), nil, "")...); err != nil { - return err - } - if err := d.exec(ctx, tx, classCitationCreate); err != nil { - return err - } - if err := d.prepare(ctx, tx, classCitationInsert(), columns(l, classCitationFields(), nil, classCitationUnwrap)...); err != nil { - return err - } */ default: log.Println(l.Table.Name()) diff --git a/meta/sqlite/gauge.go b/meta/sqlite/gauge.go new file mode 100644 index 000000000..24221d1c8 --- /dev/null +++ b/meta/sqlite/gauge.go @@ -0,0 +1,101 @@ +package sqlite + +import ( + "fmt" +) + +const gaugeCreate = ` +DROP TABLE IF EXISTS gauge; +CREATE TABLE IF NOT EXISTS gauge ( + gauge_id INTEGER PRIMARY KEY NOT NULL, + gauge TEXT NOT NULL, + identification_number TEXT NOT NULL, + analysis_time_zone REAL NOT NULL, + analysis_latitude REAL NOT NULL, + analysis_longitude REAL NOT NULL, + crex_tag TEXT NOT NULL, + start_date DATETIME NOT NULL CHECK (start_date IS strftime('%Y-%m-%dT%H:%M:%SZ', start_date)), + end_date DATETIME NOT NULL CHECK (end_date IS strftime('%Y-%m-%dT%H:%M:%SZ', end_date)), + UNIQUE(gauge, start_date, end_date) +); +` + +var gauge = Table{ + Create: gaugeCreate, + Select: func() string { + return "SELECT gauge_id FROM gauge WHERE gauge = ?" + }, + Insert: func() string { + return "INSERT OR IGNORE INTO gauge (gauge, identification_number, analysis_time_zone, analysis_latitude, analysis_longitude, crex_tag, start_date, end_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?);" + }, + Fields: []string{"Gauge", "Identification Number", "Analysis Time Zone", "Analysis Latitude", "Analysis Longitude", "Crex Tag", "Start Date", "End Date"}, +} + +const constituentCreate = ` +DROP TABLE IF EXISTS constituent; +CREATE TABLE IF NOT EXISTS constituent ( + constituent_id INTEGER PRIMARY KEY NOT NULL, + gauge_id INTEGER NOT NULL, + number TEXT NOT NULL, + constituent TEXT NOT NULL, + amplitude REAL NOT NULL, + lag TEXT NOT NULL, + start_date DATETIME NOT NULL CHECK (start_date IS strftime('%Y-%m-%dT%H:%M:%SZ', start_date)), + end_date DATETIME NOT NULL CHECK (end_date IS strftime('%Y-%m-%dT%H:%M:%SZ', end_date)), + FOREIGN KEY (gauge_id) REFERENCES gauge (gauge_id), + UNIQUE(gauge_id, number, start_date) +); +CREATE TRIGGER IF NOT EXISTS no_overlap_on_constituent BEFORE INSERT ON constituent +WHEN EXISTS ( + SELECT * FROM constituent + WHERE datetime(start_date) <= datetime(NEW.end_date) + AND datetime(end_date) > datetime(NEW.start_date) + AND gauge_id = NEW.gauge_id + AND number = NEW.number +) +BEGIN + SELECT RAISE(FAIL, "overlapping intervals on constituent"); +END; +` + +var constituent = Table{ + Create: constituentCreate, + Insert: func() string { + return fmt.Sprintf("INSERT OR IGNORE INTO constituent (gauge, number, constituent, amplitude, lag, start_date, end_date) VALUES ((%s), ?, ?, ?, ?, ?, ?);", + gauge.Select(), + ) + }, + Fields: []string{"Gauge", "Identification Number", "Analysis Time Zone", "Analysis Latitude", "Analysis Longitude", "Crex Tag", "Start Date", "End Date"}, +} + +const dartCreate = ` +DROP TABLE IF EXISTS dart; +CREATE TABLE IF NOT EXISTS dart ( + dart_id INTEGER PRIMARY KEY NOT NULL, + station_id TEXT NOT NULL, + pid TEXT NOT NULL, + wmo_identifier TEXT NOT NULL, + start_date DATETIME NOT NULL CHECK (start_date IS strftime('%Y-%m-%dT%H:%M:%SZ', start_date)), + end_date DATETIME NOT NULL CHECK (end_date IS strftime('%Y-%m-%dT%H:%M:%SZ', end_date)), + FOREIGN KEY (station_id) REFERENCES station (station_id), + UNIQUE(station_id, start_date, end_date) +); +CREATE TRIGGER IF NOT EXISTS no_overlap_on_dart BEFORE INSERT ON dart +WHEN EXISTS ( + SELECT * FROM dart + WHERE datetime(start_date) <= datetime(NEW.end_date) + AND datetime(end_date) > datetime(NEW.start_date) + AND station_id = NEW.station_id +) +BEGIN + SELECT RAISE(FAIL, "overlapping intervals on dart"); +END; +` + +var dart = Table{ + Create: dartCreate, + Insert: func() string { + return fmt.Sprintf("INSERT OR IGNORE INTO dart (station_id, pid, wmo_identifier, start_date, end_date) VALUES ((%s), ?, ?, ?, ?);", station.Select()) + }, + Fields: []string{"Station", "Pid", "WMO Identifier", "Start Date", "End Date"}, +} diff --git a/meta/sqlite/station.go b/meta/sqlite/station.go index eb9bfcd39..4ed1a2907 100644 --- a/meta/sqlite/station.go +++ b/meta/sqlite/station.go @@ -276,3 +276,61 @@ var feature = Table{ }, Fields: []string{"Station", "Location", "Sublocation", "Property", "Description", "Aspect", "Start Date", "End Date"}, } + +const classCreate = ` +DROP TABLE IF EXISTS class; +CREATE TABLE IF NOT EXISTS class ( + class_id INTEGER PRIMARY KEY NOT NULL, + station_id INTEGER NOT NULL, + site_class TEXT NOT NULL, + vs30 REAL NOT NULL, + vs30_quality TEXT NOT NULL, + tsite TEXT NOT NULL, + tsite_method TEXT NOT NULL, + tsite_quality TEXT NOT NULL, + basement_depth REAL NOT NULL, + depth_quality TEXT NOT NULL, + link TEXT NULL, + notes TEXT NULL, + FOREIGN KEY (station_id) REFERENCES station (station_id), + UNIQUE(station_id) +); +` + +var class = Table{ + Create: classCreate, + Select: func() string { + return fmt.Sprintf("SELECT class_id FROM class WHERE station_id = (%s)", station.Select()) + }, + Insert: func() string { + return fmt.Sprintf("INSERT OR IGNORE INTO class (station_id, site_class, vs30, vs30_quality, tsite, tsite_method, tsite_quality, basement_depth, depth_quality, link, notes) VALUES ((%s), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", station.Select()) + }, + Fields: []string{"Station", "Site Class", "Vs30", "Vs30 Quality", "Tsite", "Tsite Method", "Tsite Quality", "Basement Depth", "Depth Quality", "Link", "Notes"}, +} + +const classCitationCreate = ` +DROP TABLE IF EXISTS class_citation; +CREATE TABLE IF NOT EXISTS class_citation ( + class_citation_id INTEGER PRIMARY KEY NOT NULL, + class_id INTEGER NOT NULL, + citation_id INTEGER NOT NULL, + FOREIGN KEY (class_id) REFERENCES class (class_id), + FOREIGN KEY (citation_id) REFERENCES citation (citation_id), + UNIQUE (class_id, citation_id) +);` + +var classCitations = Table{ + Create: classCitationCreate, + Select: func() string { + return fmt.Sprintf("SELECT class_citation_id FROM class_citation WHERE class_id = (%s) AND citation_id = (%s)", + class.Select(), citation.Select(), + ) + }, + Insert: func() string { + return fmt.Sprintf("INSERT INTO class_citation (class_id, citation_id) VALUES ((%s), (%s));", + class.Select(), citation.Select(), + ) + }, + Fields: []string{"Station", "Citations"}, + Unwrap: "Citations", +} diff --git a/meta/sqlite/table.go b/meta/sqlite/table.go new file mode 100644 index 000000000..9c669a88d --- /dev/null +++ b/meta/sqlite/table.go @@ -0,0 +1,120 @@ +package sqlite + +import ( + "strings" + + "github.com/GeoNet/delta/meta" +) + +type Table struct { + // Create holds the SQL code needed to create the table and all associated + // triggers and constraints. + Create string + // Select holds the prepared statement that can be used to select the primary + // key from the table. + Select func() string + // Insert holds the full prepared statement to insert a row into the table. + Insert func() string + // Fields gives the delta csv column names to use for inserting a row into the table. + Fields []string + // Nulls holds the set of columds that are allowed to be NULL in the table, an empty + // string in the CSV field will indicate a NULL value should be passed into the row. + Nulls []string + // Unwrap can be used to build a linking table when a column has multiple fields + Unwrap string +} + +// Links returns the rows to insert into a Linking table for the given unwrapping column. +func (t Table) Links(list meta.TableList) [][]any { + + lines := list.Table.Encode(list.List) + if !(len(lines) > 0) { + return nil + } + + lookup := make(map[string]int) + for n, v := range list.Table.Columns() { + lookup[v] = n + } + + w, ok := lookup[t.Unwrap] + if !ok { + return nil + } + + var res [][]any + for _, line := range lines[1:] { + if !(w < len(line)) { + continue + } + for _, c := range strings.Fields(strings.TrimSpace(line[w])) { + var parts []any + for _, f := range t.Fields { + n, ok := lookup[f] + if !ok { + return nil + } + if !(n < len(line)) { + return nil + } + switch { + case n == w: + parts = append(parts, c) + default: + parts = append(parts, line[n]) + } + } + res = append(res, parts) + } + } + return res +} + +// checkNull returns null if the value is empty and is a member +// of the nulls map, otherwise it returns the value. +func checkNull(nulls map[string]interface{}, key, value string) any { + if _, ok := nulls[key]; ok && value == "" { + return nil + } + return value +} + +// Columns returns the expected rows for the given TableList. +func (t Table) Columns(list meta.TableList) [][]any { + + lines := list.Table.Encode(list.List) + if !(len(lines) > 0) { + return nil + } + + nulls := make(map[string]interface{}) + for _, v := range t.Nulls { + nulls[v] = true + } + + lookup := make(map[string]int) + for n, v := range list.Table.Columns() { + lookup[v] = n + } + + var res [][]any + for _, line := range lines[1:] { + var parts []any + + for _, f := range t.Fields { + n, ok := lookup[f] + if !ok { + return nil + } + if !(n < len(line)) { + return nil + } + + parts = append(parts, checkNull(nulls, f, line[n])) + } + + res = append(res, parts) + } + + return res +}