Amalegeni-Go
 

Example 4 : copy data from Postgres to Mysql

Use case : you have data lingering about in a postgres database, which you want to copy selectively to a mysql database.

For example, we have music album/tracks data in two tables in a postgres db, and want to select the tracks that contain the word 'streicher' in their title, and store this data in one table in a mysql db.

The major change to prior examples is that here we use the gq template instead of the se template. The gq template is typically used for writing go/sql programs for massaging or copying data, where you either need a bit more iterative logic than a sql script can offer or to copy data between different databases. Futher more, the generated code is self-contained, ie. it has everything to produce an executable, since the focus is on writing as little code as possible.

The code

This is all the code you need to write. Amalegeni-go will generate the rest for you. Then just compile and execute!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func Run() {{
    postgres:=0
    mysql:=1
    Insert( Select("%STREICHER%", postgres), mysql )
}} 
run 


type Track struct {
    AlbumTitle string
    Artist string 
    TrackTitle string
} 


func Select(titleLike string, d dbid) []Track
<< 
    select coalesce(a.title,'No title'),
           coalesce(t.artist, coalesce(a.artist, 'Unknown') ),
           coalesce(t.title,'No title')
    from   t_album a, t_track t 
    where  a.album_id=t.album_id
    and    t.title_uc like $1
>>


func Insert(tsl []Track, d dbid) string
<< 
    insert into t_selection ( album_title, artist, track_title)
    values (?,?,?) 
>>

The Select() function (line 16) returns a slice of Track objects, and a Track is a struct defined in line 9. The d argument is of type dbid, which is under the hood translated into the type int. This dbid parameter is not passed to the SQL statement, but to the GetDBH() function, to determine which database to connect to. The convention uses in the above example is that 0 stands for the postgres db, and 1 stands for the mysql db. Assigning these values to aptly named variables increases legibility.

The Insert() function (line 27): not much new here, but do note that this sql is targetted at MySQL since it uses the question mark as placeholders for the parameters (see line 30). The select sql, targetted at postgresql, uses the $1,$2,.. notation (eg. see line 23).

Note: the above program is called transfuse-gq.amg in the zip file (see 'Setup instructions' further down).

The scenic tour: some other examples

Here are some other programs included in the zip file (see 'Setup instructions' further down).

Count

Count the records in the source and target tables. Good for testing if your databases are correctly setup.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func Run() {{
    postgres:=0
    mysql:=1

    fmt.Printf("SRC: Albums    %d\n", CountAlbum(postgres) ) 
    fmt.Printf("SRC: Tracks    %d\n", CountTrack(postgres) ) 

    fmt.Printf("TRG: Selection %d\n", CountSelection(mysql) ) 
}} 
run 


func CountAlbum(d dbid) int
<< 
    select count(1)
    from   t_album 
>>


func CountTrack(d dbid) int
<< 
    select count(1)
    from   t_track 
>>


func CountSelection(d dbid) int
<< 
    select count(1)
    from   t_selection
>>

Show

Show the data of the t_selection table in the target database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func Run() {{
    mysql:=1
    
    for _,t := range( Select(mysql) ) {
        fmt.Printf("A:%-30s a:%-30s t:%s\n", 
                t.AlbumTitle, 
                t.Artist, 
                t.TrackTitle)
    }
}} 
run 


type Track struct {
    AlbumTitle string
    Artist string 
    TrackTitle string
} 


func Select(d dbid) []Track 
<<  
    select  left(album_title,30), 
            left(artist,30), 
            left(track_title,30)
    from t_selection
>>

Delete

Delete the data from the target table.

1
2
3
4
5
6
7
8
9
10
func Run() {{
    mysql:=1
    DeleteTargetData(mysql) 
}}  
run     
                
func DeleteTargetData(d dbid) 
<<              
    delete from t_selection 
>>

Setup instructions

Prerequisites

  • you need to have Go installed
  • the drivers for the postgresql and mysql database are needed, so go get the following drivers (I put go github code in my $HOME/go_github directory) :
    • github.com/lib/pq
    • github.com/go-sql-driver/mysql
  • grab the 'amgo' version for your system: see amalegeni-go download and put it in your $HOME/bin or /usr/local/bin.

Download the code and data

Download and unpack ex04.zip that contains the code and data.

$ unzip ex04.zip
$ cd copy_postgres_to_mysql

Database setup

See the db/setup_postgresql.txt and db/setup_mysql.txt file in the zipfile, on how to setup both databases. Data is included for copying into the source database.

$ vi db/setup_postgresql.txt 
$ vi db/setup_mysql.txt

Compile

$ bash compile_all.sh 

The compiled code should now be in the bin directory.

Execute

$ . ./setenv.sh 

$ count
SRC: Albums    677
SRC: Tracks    10014
TRG: Selection 0

$ transfuse

$ count
SRC: Albums    677
SRC: Tracks    10014
TRG: Selection 26

$ show
..
..
A:Les 32 Sonates Pour Piano  a:7. largo allegro t:fuga a tre voci
A:Les 32 Sonates Pour Piano  a:7. largo allegro t:fuga a tre voci
A:Italienische Flötenkonzert a:Antonio Vivaldi  t:Konzert C-dur für zwei Flöten 
A:Italienische Flötenkonzert a:Antonio Vivaldi  t:Konzert C-dur für zwei Flöten 
A:Violinkonzerte             a:Antonio Vivaldi  t:Violine, Streicher und Basso 
A:Violinkonzerte             a:Antonio Vivaldi  t:Violine, Streicher und Basso 
..
..

The amalegeni-go template

The template used in above examples is the gq template, with a minor change to the GetDBH() function.

If you don't use the template of the above zip file, you can also reproduce it like this:

$ cd <your_project_dir> 
$ mkdir template
$ amgo -s gq > template/gq.tpl

And then find the GetDBH() function in the gq.tpl file, and replace it by :

1
2
3
4
5
6
7
8
9
func GetDBH(d int) (dbh *sql.DB, err error) {
    if (d==0) {
        dbh, err = sql.Open("postgres", 
                "dbname=src_db user=src_usr password=src_pwd port=5432 host=localhost")
    } else if (d==1) {
        dbh, err = sql.Open("mysql", "trg_usr:trg_pwd@/trg_db")
    }
    return dbh, err
}
 
© Willem Moors, 2013 - 2020