@@ -6,83 +6,114 @@ import "strings"
66import "database/sql"
77import "github.com/joncrlsn/pgutil"
88
9- // ColumnSchema holds a channel streaming column data from one of the databases as well as
9+ // ==================================
10+ // ColumnRows definition
11+ // ==================================
12+ type ColumnRows []map [string ]string
13+
14+ func (slice ColumnRows ) Len () int {
15+ return len (slice )
16+ }
17+
18+ func (slice ColumnRows ) Less (i , j int ) bool {
19+ if slice [i ]["table_name" ] < slice [j ]["table_name" ] {
20+ return true
21+ }
22+ return slice [i ]["column_name" ] < slice [j ]["column_name" ]
23+ }
24+
25+ func (slice ColumnRows ) Swap (i , j int ) {
26+ slice [i ], slice [j ] = slice [j ], slice [i ]
27+ }
28+
29+ // ==================================
30+ // ColumnSchema definition
31+ // (implements Schema -- defined in pgdiff.go)
32+ // ==================================
33+
34+ // ColumnSchema holds a slice of rows from one of the databases as well as
1035// a reference to the current row of data we're viewing.
11- //
12- // ColumnSchema implements the Schema interface defined in pgdiff.go
1336type ColumnSchema struct {
14- channel chan map [ string ] string
15- row map [ string ] string
16- done bool
37+ rows ColumnRows
38+ rowNum int
39+ done bool
1740}
1841
19- // NextRow reads from the channel and tells you if there are (probably) more or not
42+ // get returns the value from the current row for the given key
43+ func (c * ColumnSchema ) get (key string ) string {
44+ if c .rowNum >= len (c .rows ) {
45+ return ""
46+ }
47+ return c.rows [c.rowNum ][key ]
48+ }
49+
50+ // NextRow increments the rowNum and tells you whether or not there are more
2051func (c * ColumnSchema ) NextRow () bool {
21- c .row = <- c .channel
22- if len (c .row ) == 0 {
52+ if c .rowNum >= len (c .rows )- 1 {
2353 c .done = true
2454 }
55+ c .rowNum = c .rowNum + 1
2556 return ! c .done
2657}
2758
2859// Compare tells you, in one pass, whether or not the first row matches, is less than, or greater than the second row
29- func (c ColumnSchema ) Compare (obj interface {}) int {
60+ func (c * ColumnSchema ) Compare (obj interface {}) int {
3061 c2 , ok := obj .(* ColumnSchema )
3162 if ! ok {
3263 fmt .Println ("Error!!!, change needs a ColumnSchema instance" , c2 )
3364 }
3465
35- val := _compareString (c .row [ "table_name" ] , c2 .row [ "table_name" ] )
66+ val := _compareString (c .get ( "table_name" ) , c2 .get ( "table_name" ) )
3667 if val != 0 {
3768 // Table name differed so return that value
3869 return val
3970 }
4071
4172 // Table name was the same so compare column name
42- val = _compareString (c .row [ "column_name" ] , c2 .row [ "column_name" ] )
73+ val = _compareString (c .get ( "column_name" ) , c2 .get ( "column_name" ) )
4374 return val
4475}
4576
46- // Add returns SQL to add the column
47- func (c ColumnSchema ) Add () {
48- if c .row [ "data_type" ] == "character varying" {
49- maxLength , valid := getMaxLength (c .row [ "character_maximum_length" ] )
77+ // Add prints SQL to add the column
78+ func (c * ColumnSchema ) Add () {
79+ if c .get ( "data_type" ) == "character varying" {
80+ maxLength , valid := getMaxLength (c .get ( "character_maximum_length" ) )
5081 if ! valid {
5182 fmt .Println ("-- WARNING: varchar column has no maximum length. Set to 1024" )
5283 }
53- fmt .Printf ("ALTER TABLE %s ADD COLUMN %s %s(%s)" , c .row [ "table_name" ] , c .row [ "column_name" ] , c .row [ "data_type" ] , maxLength )
84+ fmt .Printf ("ALTER TABLE %s ADD COLUMN %s %s(%s)" , c .get ( "table_name" ) , c .get ( "column_name" ) , c .get ( "data_type" ) , maxLength )
5485 } else {
55- fmt .Printf ("ALTER TABLE %s ADD COLUMN %s %s" , c .row [ "table_name" ] , c .row [ "column_name" ] , c .row [ "data_type" ] )
86+ fmt .Printf ("ALTER TABLE %s ADD COLUMN %s %s" , c .get ( "table_name" ) , c .get ( "column_name" ) , c .get ( "data_type" ) )
5687 }
5788
58- if c .row [ "is_nullable" ] == "NO" {
89+ if c .get ( "is_nullable" ) == "NO" {
5990 fmt .Printf (" NOT NULL" )
6091 }
61- if c .row [ "column_default" ] != "null" {
62- fmt .Printf (" DEFAULT %s" , c .row [ "column_default" ] )
92+ if c .get ( "column_default" ) != "null" {
93+ fmt .Printf (" DEFAULT %s" , c .get ( "column_default" ) )
6394 }
6495 fmt .Printf (";\n " )
6596}
6697
67- // Drop returns SQL to drop the column
68- func (c ColumnSchema ) Drop () {
98+ // Drop prints SQL to drop the column
99+ func (c * ColumnSchema ) Drop () {
69100 // if dropping column
70- fmt .Printf ("ALTER TABLE %s DROP COLUMN IF EXISTS %s;\n " , c .row [ "table_name" ] , c .row [ "column_name" ] )
101+ fmt .Printf ("ALTER TABLE %s DROP COLUMN IF EXISTS %s;\n " , c .get ( "table_name" ) , c .get ( "column_name" ) )
71102}
72103
73104// Change handles the case where the table and column match, but the details do not
74- func (c ColumnSchema ) Change (obj interface {}) {
105+ func (c * ColumnSchema ) Change (obj interface {}) {
75106 c2 , ok := obj .(* ColumnSchema )
76107 if ! ok {
77108 fmt .Println ("Error!!!, ColumnSchema.Change(obj) needs a ColumnSchema instance" , c2 )
78109 }
79110
80111 // Detect column type change (mostly varchar length, or number size increase) (integer to/from bigint is OK)
81- if c .row [ "data_type" ] == c2 .row [ "data_type" ] {
82- if c .row [ "data_type" ] == "character varying" {
83- max1 , max1Valid := getMaxLength (c .row [ "character_maximum_length" ] )
84- max2 , max2Valid := getMaxLength (c2 .row [ "character_maximum_length" ] )
85- if (max1Valid || ! max2Valid ) && (max1 != c2 .row [ "character_maximum_length" ] ) {
112+ if c .get ( "data_type" ) == c2 .get ( "data_type" ) {
113+ if c .get ( "data_type" ) == "character varying" {
114+ max1 , max1Valid := getMaxLength (c .get ( "character_maximum_length" ) )
115+ max2 , max2Valid := getMaxLength (c2 .get ( "character_maximum_length" ) )
116+ if (max1Valid || ! max2Valid ) && (max1 != c2 .get ( "character_maximum_length" ) ) {
86117 if ! max1Valid {
87118 fmt .Println ("-- WARNING: varchar column has no maximum length. Setting to 1024" )
88119 }
@@ -93,44 +124,48 @@ func (c ColumnSchema) Change(obj interface{}) {
93124 if max1Int < max2Int {
94125 fmt .Println ("-- WARNING: The next statement will shorten a character varying column." )
95126 }
96- fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s TYPE character varying(%s);\n " , c .row [ "table_name" ] , c .row [ "column_name" ] , max1 )
127+ fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s TYPE character varying(%s);\n " , c .get ( "table_name" ) , c .get ( "column_name" ) , max1 )
97128 }
98129 }
99130 }
100131
101132 // TODO: Code and test a column change from integer to bigint
102- if c .row [ "data_type" ] != c2 .row [ "data_type" ] {
103- fmt .Printf ("-- WARNING: This type change may not work well: (%s to %s).\n " , c2 .row [ "data_type" ] , c .row [ "data_type" ] )
104- if strings .HasPrefix (c .row [ "data_type" ] , "character" ) {
105- max1 , max1Valid := getMaxLength (c .row [ "character_maximum_length" ] )
133+ if c .get ( "data_type" ) != c2 .get ( "data_type" ) {
134+ fmt .Printf ("-- WARNING: This type change may not work well: (%s to %s).\n " , c2 .get ( "data_type" ) , c .get ( "data_type" ) )
135+ if strings .HasPrefix (c .get ( "data_type" ) , "character" ) {
136+ max1 , max1Valid := getMaxLength (c .get ( "character_maximum_length" ) )
106137 if ! max1Valid {
107138 fmt .Println ("-- WARNING: varchar column has no maximum length. Setting to 1024" )
108139 }
109- fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s TYPE %s(%s);\n " , c .row [ "table_name" ] , c .row [ "column_name" ] , c .row [ "data_type" ] , max1 )
140+ fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s TYPE %s(%s);\n " , c .get ( "table_name" ) , c .get ( "column_name" ) , c .get ( "data_type" ) , max1 )
110141 } else {
111- fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s TYPE %s;\n " , c .row [ "table_name" ] , c .row [ "column_name" ] , c .row [ "data_type" ] )
142+ fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s TYPE %s;\n " , c .get ( "table_name" ) , c .get ( "column_name" ) , c .get ( "data_type" ) )
112143 }
113144 }
114145
115146 // Detect column default change (or added, dropped)
116- if c .row [ "column_default" ] == "null" {
117- if c .row [ "column_default" ] != "null" {
118- fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT;\n " , c .row [ "table_name" ] , c .row [ "column_name" ] )
147+ if c .get ( "column_default" ) == "null" {
148+ if c .get ( "column_default" ) != "null" {
149+ fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT;\n " , c .get ( "table_name" ) , c .get ( "column_name" ) )
119150 }
120- } else if c .row [ "column_default" ] != c2 .row [ "column_default" ] {
121- fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s;\n " , c .row [ "table_name" ] , c .row [ "column_name" ] , c .row [ "column_default" ] )
151+ } else if c .get ( "column_default" ) != c2 .get ( "column_default" ) {
152+ fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s;\n " , c .get ( "table_name" ) , c .get ( "column_name" ) , c .get ( "column_default" ) )
122153 }
123154
124155 // TODO Detect not-null and nullable change
125- if c .row [ "is_nullable" ] != c2 .row [ "is_nullable" ] {
126- if c .row [ "is_nullable" ] == "YES" {
127- fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL;\n " , c .row [ "table_name" ] , c .row [ "column_name" ] )
156+ if c .get ( "is_nullable" ) != c2 .get ( "is_nullable" ) {
157+ if c .get ( "is_nullable" ) == "YES" {
158+ fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL;\n " , c .get ( "table_name" ) , c .get ( "column_name" ) )
128159 } else {
129- fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s SET NOT NULL;\n " , c .row [ "table_name" ] , c .row [ "column_name" ] )
160+ fmt .Printf ("ALTER TABLE %s ALTER COLUMN %s SET NOT NULL;\n " , c .get ( "table_name" ) , c .get ( "column_name" ) )
130161 }
131162 }
132163}
133164
165+ // ==================================
166+ // Functions
167+ // ==================================
168+
134169/*
135170 * Compare the columns in the two databases
136171 */
@@ -150,15 +185,27 @@ ORDER by table_name, column_name COLLATE "C" ASC;`
150185 rowChan1 , _ := pgutil .QueryStrings (conn1 , sql )
151186 rowChan2 , _ := pgutil .QueryStrings (conn2 , sql )
152187
153- // We have to explicitly type this as Schema for some reason
154- var schema1 Schema = & ColumnSchema {channel : rowChan1 }
155- var schema2 Schema = & ColumnSchema {channel : rowChan2 }
188+ //rows1 := make([]map[string]string, 500)
189+ rows1 := make (ColumnRows , 500 )
190+ for row := range rowChan1 {
191+ rows1 = append (rows1 , row )
192+ }
193+
194+ //rows2 := make([]map[string]string, 500)
195+ rows2 := make (ColumnRows , 500 )
196+ for row := range rowChan2 {
197+ rows2 = append (rows2 , row )
198+ }
199+
200+ // We have to explicitly type this as Schema here for some unknown reason
201+ var schema1 Schema = & ColumnSchema {rows : rows1 , rowNum : - 1 }
202+ var schema2 Schema = & ColumnSchema {rows : rows2 , rowNum : - 1 }
156203
157204 // Compare the columns
158205 doDiff (schema1 , schema2 )
159206}
160207
161- // getMaxLength returns the maximum length and whether or not it is valie
208+ // getMaxLength returns the maximum length and whether or not it is valid
162209func getMaxLength (maxLength string ) (string , bool ) {
163210
164211 if maxLength == "null" {
0 commit comments