@@ -7,6 +7,7 @@ use bytes::{BufMut, Bytes, BytesMut};
77use chrono:: { DateTime , TimeZone , Utc } ;
88use database:: pool:: { postgres, sqlite, ConnectionManager } ;
99use futures_util:: sink:: SinkExt ;
10+ use hashbrown:: HashMap ;
1011use serde:: { Serialize , Serializer } ;
1112use std:: convert:: { TryFrom , TryInto } ;
1213use std:: io:: Write ;
@@ -21,6 +22,9 @@ trait Table {
2122 /// Comma-separated list of table's attribute names in SQLite.
2223 fn sqlite_attributes ( ) -> & ' static str ;
2324
25+ /// Comma-separated list of table's attribute names in Postgres.
26+ fn postgres_attributes ( ) -> & ' static str ;
27+
2428 /// Name of `generated always as identity` attribute in Postgres,
2529 /// if applicable.
2630 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > ;
@@ -56,6 +60,10 @@ impl Table for Artifact {
5660 "id, name, date, type"
5761 }
5862
63+ fn postgres_attributes ( ) -> & ' static str {
64+ "id, name, date, type"
65+ }
66+
5967 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
6068 Some ( "id" )
6169 }
@@ -92,6 +100,10 @@ impl Table for ArtifactCollectionDuration {
92100 "aid, date_recorded, duration"
93101 }
94102
103+ fn postgres_attributes ( ) -> & ' static str {
104+ "aid, date_recorded, duration"
105+ }
106+
95107 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
96108 None
97109 }
@@ -128,6 +140,10 @@ impl Table for Benchmark {
128140 "name, stabilized, category"
129141 }
130142
143+ fn postgres_attributes ( ) -> & ' static str {
144+ "name, stabilized, category"
145+ }
146+
131147 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
132148 None
133149 }
@@ -160,6 +176,10 @@ impl Table for Collection {
160176 "id, perf_commit"
161177 }
162178
179+ fn postgres_attributes ( ) -> & ' static str {
180+ "id, perf_commit"
181+ }
182+
163183 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
164184 Some ( "id" )
165185 }
@@ -193,6 +213,10 @@ impl Table for CollectorProgress {
193213 "aid, step, start, end"
194214 }
195215
216+ fn postgres_attributes ( ) -> & ' static str {
217+ "aid, step, start_time, end_time"
218+ }
219+
196220 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
197221 None
198222 }
@@ -232,6 +256,10 @@ impl Table for Error {
232256 "aid, benchmark, error"
233257 }
234258
259+ fn postgres_attributes ( ) -> & ' static str {
260+ "aid, benchmark, error"
261+ }
262+
235263 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
236264 None
237265 }
@@ -266,6 +294,10 @@ impl Table for Pstat {
266294 "series, aid, cid, value"
267295 }
268296
297+ fn postgres_attributes ( ) -> & ' static str {
298+ "series, aid, cid, value"
299+ }
300+
269301 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
270302 None
271303 }
@@ -303,6 +335,10 @@ impl Table for PstatSeries {
303335 "id, crate, profile, scenario, backend, metric"
304336 }
305337
338+ fn postgres_attributes ( ) -> & ' static str {
339+ "id, crate, profile, scenario, backend, metric"
340+ }
341+
306342 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
307343 Some ( "id" )
308344 }
@@ -345,6 +381,10 @@ impl Table for PullRequestBuild {
345381 "bors_sha, pr, parent_sha, complete, requested, include, exclude, runs, commit_date"
346382 }
347383
384+ fn postgres_attributes ( ) -> & ' static str {
385+ "bors_sha, pr, parent_sha, complete, requested, include, exclude, runs, commit_date"
386+ }
387+
348388 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
349389 None
350390 }
@@ -393,6 +433,10 @@ impl Table for RawSelfProfile {
393433 "aid, cid, crate, profile, cache"
394434 }
395435
436+ fn postgres_attributes ( ) -> & ' static str {
437+ "aid, cid, crate, profile, cache"
438+ }
439+
396440 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
397441 None
398442 }
@@ -429,6 +473,10 @@ impl Table for RustcCompilation {
429473 "aid, cid, crate, duration"
430474 }
431475
476+ fn postgres_attributes ( ) -> & ' static str {
477+ "aid, cid, crate, duration"
478+ }
479+
432480 fn postgres_generated_id_attribute ( ) -> Option < & ' static str > {
433481 None
434482 }
@@ -597,10 +645,24 @@ async fn copy<T: Table>(
597645 // unlikely that we will ever execute SQL built from external strings.
598646 let table = T :: name ( ) ;
599647
648+ let postgres_columns = postgres
649+ . query (
650+ r#"SELECT column_name FROM information_schema.columns
651+ WHERE table_schema = 'public' AND table_name = $1
652+ ORDER BY ordinal_position"# ,
653+ & [ & table] ,
654+ )
655+ . await
656+ . unwrap ( )
657+ . into_iter ( )
658+ . map ( |row| row. get ( 0 ) )
659+ . collect :: < Vec < String > > ( ) ;
660+ let attributes = mapping_pg_columns_to_attributes :: < T > ( & postgres_columns) ;
661+
600662 let copy = postgres
601663 . prepare ( & format ! (
602- r#"copy {} from stdin (encoding utf8, format csv, null '{}')"# ,
603- table, NULL_STRING ,
664+ r#"copy {} ({}) from stdin (encoding utf8, format csv, null '{}')"# ,
665+ table, attributes , NULL_STRING ,
604666 ) )
605667 . await
606668 . unwrap ( ) ;
@@ -707,6 +769,46 @@ fn postgres_csv_writer<W: Write>(w: W) -> csv::Writer<W> {
707769 csv:: WriterBuilder :: new ( ) . has_headers ( false ) . from_writer ( w)
708770}
709771
772+ /// # Panics
773+ /// Panics if the number of `sqlite_attributes` and `postgres_attributes` is mismatched or
774+ /// a corresponding attribute for the column is not found.
775+ fn mapping_pg_columns_to_attributes < T : Table > ( postgres_columns : & [ impl AsRef < str > ] ) -> String {
776+ // We assume that the attributes between SQLite and Postgres have equal lengths.
777+ // Additionally, the attribute names of Postgres should exactly match the column names in the
778+ // database. We then attempt to reorder the attributes to mitigate the ordering difference
779+ // between versions.
780+ let sl = T :: sqlite_attributes ( )
781+ . split ( ',' )
782+ . map ( str:: trim)
783+ . collect :: < Vec < _ > > ( ) ;
784+ let pg = T :: postgres_attributes ( )
785+ . split ( ',' )
786+ . map ( str:: trim)
787+ . collect :: < Vec < _ > > ( ) ;
788+ assert_eq ! (
789+ sl. len( ) ,
790+ pg. len( ) ,
791+ "The number of attributes in SQLite and Postgres is mismatched."
792+ ) ;
793+ let map = pg
794+ . iter ( )
795+ . enumerate ( )
796+ . map ( |( i, p) | ( p, i) )
797+ . collect :: < HashMap < _ , _ > > ( ) ;
798+ let mut out_attrs = vec ! [ "" ; pg. len( ) ] ;
799+ for col in postgres_columns. iter ( ) . map ( AsRef :: as_ref) {
800+ let idx = map. get ( & col) . unwrap_or_else ( || {
801+ panic ! (
802+ "Failed to find a corresponding attribute for column {} in table {}." ,
803+ col,
804+ T :: name( )
805+ )
806+ } ) ;
807+ out_attrs[ * idx] = col;
808+ }
809+ out_attrs. join ( ", " )
810+ }
811+
710812async fn get_tables ( postgres : & tokio_postgres:: Transaction < ' _ > ) -> Vec < String > {
711813 postgres
712814 . query (
0 commit comments