Skip to content
This repository was archived by the owner on Jan 23, 2020. It is now read-only.

Commit 00cc874

Browse files
committed
fixing issue ps0uth#9, adding travis-ci
1 parent 1c8d44a commit 00cc874

File tree

12 files changed

+119
-71
lines changed

12 files changed

+119
-71
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ MANIFEST
1313
.\#*
1414
*~
1515
*.egg-info
16+
mysql2pgsql.yml
1617
*.yaml
17-
*.yml
1818
docs/_build
1919
tests/reports

.travis.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
language: python
2+
python:
3+
- "2.6"
4+
- "2.7"
5+
install:
6+
- pip install . --use-mirrors
7+
- pip install -r requirements.txt --use-mirrors
8+
script: nosetests
9+
before_script:
10+
- mysql -e 'create database mysql2pgsql;'
11+
- psql -c 'create database mysql2pgsql;' -U postgres
12+
env:
13+
- DB=mysql
14+
- DB=postgres
15+
branches:
16+
only:
17+
- master
18+
- develop
19+
notifications:
20+
email:
21+

bin/py-mysql2pgsql

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,33 @@
11
#! /usr/bin/env python
22
import os
33
import sys
4+
import argparse
45
import mysql2pgsql
56
from mysql2pgsql.lib.errors import ConfigurationFileInitialized
67

78
if __name__ == '__main__':
89
description = 'Tool for migrating/converting data from mysql to postgresql.'
910
epilog = 'https://github.com/philipsoutham/py-mysql2pgsql'
10-
try:
11-
import argparse
12-
parser = argparse.ArgumentParser(
13-
description=description,
14-
epilog=epilog)
15-
parser.add_argument(
16-
'-v', '--verbose',
17-
action='store_true',
18-
help='Show progress of data migration.'
19-
)
20-
parser.add_argument(
21-
'-f', '--file',
22-
default='mysql2pgsql.yml',
23-
help='Location of configuration file (default: %(default)s). If none exists at that path, one will be created for you.',
24-
)
25-
parser.add_argument(
26-
'-V', '--version',
27-
action='store_true',
28-
help='Print version and exit.'
29-
)
30-
options = parser.parse_args()
31-
except ImportError:
32-
import optparse
33-
parser = optparse.OptionParser(
34-
description=description,
35-
epilog=epilog)
36-
parser.add_argument(
37-
'-v', '--verbose',
38-
action='store_true',
39-
help='Show progress of data migration.'
40-
)
41-
parser.add_argument(
42-
'-f', '--file',
43-
default='mysql2pgsql.yml',
44-
help='Location of configuration file (default: %default). If none exists at that path, one will be created for you.',
45-
)
46-
parser.add_argument(
47-
'-V', '--version',
48-
action='store_true',
49-
help='Print version and exit.'
50-
)
51-
options, args = parser.parse_args()
11+
12+
parser = argparse.ArgumentParser(
13+
description=description,
14+
epilog=epilog)
15+
parser.add_argument(
16+
'-v', '--verbose',
17+
action='store_true',
18+
help='Show progress of data migration.'
19+
)
20+
parser.add_argument(
21+
'-f', '--file',
22+
default='mysql2pgsql.yml',
23+
help='Location of configuration file (default: %(default)s). If none exists at that path, one will be created for you.',
24+
)
25+
parser.add_argument(
26+
'-V', '--version',
27+
action='store_true',
28+
help='Print version and exit.'
29+
)
30+
options = parser.parse_args()
5231

5332
if options.version:
5433
# Someone wants to know the version, print and exit

mysql2pgsql/lib/postgres_db_writer.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def readline(self, *args, **kwargs):
5252
try:
5353
return '%s\n' % ('\t'.join(row))
5454
except UnicodeDecodeError:
55-
return '%s\n' % ('\t'.join(row)).decode('utf-8')
55+
return '%s\n' % ('\t'.join(r.decode('utf8') for r in row))
5656
finally:
5757
if self.verbose:
5858
if (self.idx % 20000) == 0:
@@ -69,6 +69,7 @@ def read(self, *args, **kwargs):
6969
return self.readline(*args, **kwargs)
7070

7171
def __init__(self, db_options, verbose=False):
72+
super(PostgresDbWriter, self).__init__()
7273
self.verbose = verbose
7374
self.db_options = {
7475
'host': db_options['hostname'],
@@ -133,7 +134,7 @@ def truncate(self, table):
133134
134135
Returns None
135136
"""
136-
truncate_sql, serial_key_sql = super(self.__class__, self).truncate(table)
137+
truncate_sql, serial_key_sql = super(PostgresDbWriter, self).truncate(table)
137138
self.execute(truncate_sql)
138139
if serial_key_sql:
139140
self.execute(serial_key_sql)
@@ -147,7 +148,7 @@ def write_table(self, table):
147148
148149
Returns None
149150
"""
150-
table_sql, serial_key_sql = super(self.__class__, self).write_table(table)
151+
table_sql, serial_key_sql = super(PostgresDbWriter, self).write_table(table)
151152
for sql in serial_key_sql + table_sql:
152153
self.execute(sql)
153154

@@ -160,7 +161,7 @@ def write_indexes(self, table):
160161
161162
Returns None
162163
"""
163-
index_sql = super(self.__class__, self).write_indexes(table)
164+
index_sql = super(PostgresDbWriter, self).write_indexes(table)
164165
for sql in index_sql:
165166
self.execute(sql)
166167

@@ -173,7 +174,7 @@ def write_constraints(self, table):
173174
174175
Returns None
175176
"""
176-
constraint_sql = super(self.__class__, self).write_constraints(table)
177+
constraint_sql = super(PostgresDbWriter, self).write_constraints(table)
177178
for sql in constraint_sql:
178179
self.execute(sql)
179180

mysql2pgsql/lib/postgres_file_writer.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class PostgresFileWriter(PostgresWriter):
2121
verbose = None
2222

2323
def __init__(self, output_file, verbose=False):
24+
super(PostgresFileWriter, self).__init__()
2425
self.verbose = verbose
2526
self.f = output_file
2627
self.f.write("""
@@ -40,7 +41,7 @@ def truncate(self, table):
4041
4142
Returns None
4243
"""
43-
truncate_sql, serial_key_sql = super(self.__class__, self).truncate(table)
44+
truncate_sql, serial_key_sql = super(PostgresFileWriter, self).truncate(table)
4445
self.f.write("""
4546
-- TRUNCATE %(table_name)s;
4647
%(truncate_sql)s
@@ -61,7 +62,7 @@ def write_table(self, table):
6162
6263
Returns None
6364
"""
64-
table_sql, serial_key_sql = super(self.__class__, self).write_table(table)
65+
table_sql, serial_key_sql = super(PostgresFileWriter, self).write_table(table)
6566
if serial_key_sql:
6667
self.f.write("""
6768
%(serial_key_sql)s
@@ -86,7 +87,7 @@ def write_indexes(self, table):
8687
8788
Returns None
8889
"""
89-
self.f.write('\n'.join(super(self.__class__, self).write_indexes(table)))
90+
self.f.write('\n'.join(super(PostgresFileWriter, self).write_indexes(table)))
9091

9192
@status_logger
9293
def write_constraints(self, table):
@@ -97,7 +98,7 @@ def write_constraints(self, table):
9798
9899
Returns None
99100
"""
100-
self.f.write('\n'.join(super(self.__class__, self).write_constraints(table)))
101+
self.f.write('\n'.join(super(PostgresFileWriter, self).write_constraints(table)))
101102

102103
@status_logger
103104
def write_contents(self, table, reader):
@@ -129,22 +130,22 @@ def write_contents(self, table, reader):
129130
start_time = tt()
130131
prev_val_len = 0
131132
prev_row_count = 0
132-
for i, row in enumerate(reader.read(table)):
133+
for i, row in enumerate(reader.read(table), 1):
133134
row = list(row)
134135
pr(table, row)
135136
try:
136-
f_write('%s\n' % ('\t'.join(row)))
137+
f_write(u'%s\n' % (u'\t'.join(row)))
137138
except UnicodeDecodeError:
138-
f_write('%s\n' % ('\t'.join(row)).decode('utf-8'))
139+
f_write(u'%s\n' % (u'\t'.join(r.decode('utf-8') for r in row)))
139140
if verbose:
140-
if ((i + 1) % 20000) == 0:
141+
if (i % 20000) == 0:
141142
now = tt()
142143
elapsed = now - start_time
143-
val = '%.2f rows/sec [%s] ' % (((i + 1) - prev_row_count) / elapsed, (i + 1))
144+
val = '%.2f rows/sec [%s] ' % ((i - prev_row_count) / elapsed, i)
144145
print_row_progress('%s%s' % (("\b" * prev_val_len), val))
145146
prev_val_len = len(val) + 3
146147
start_time = now
147-
prev_row_count = i + 1
148+
prev_row_count = i
148149

149150
f_write("\\.\n\n")
150151
if verbose:

mysql2pgsql/lib/postgres_writer.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,21 @@
66

77
from psycopg2.extensions import QuotedString, Binary, AsIs
88

9-
from .writer import Writer
109

11-
12-
class PostgresWriter(Writer):
10+
class PostgresWriter(object):
1311
"""Base class for :py:class:`mysql2pgsql.lib.postgres_file_writer.PostgresFileWriter`
1412
and :py:class:`mysql2pgsql.lib.postgres_db_writer.PostgresDbWriter`.
1513
"""
14+
def __init__(self):
15+
self.column_types = {}
16+
1617
def column_description(self, column):
1718
return '"%s" %s' % (column['name'], self.column_type_info(column))
1819

1920
def column_type(self, column):
20-
return self.column_type_info(column).split(" ")[0]
21+
hash_key = hash(frozenset(column.items()))
22+
self.column_types[hash_key] = self.column_type_info(column).split(" ")[0]
23+
return self.column_types[hash_key]
2124

2225
def column_type_info(self, column):
2326
"""
@@ -110,14 +113,15 @@ def process_row(self, table, row):
110113
sending to PostgreSQL via the copy command
111114
"""
112115
for index, column in enumerate(table.columns):
113-
column_type = self.column_type(column)
116+
hash_key = hash(frozenset(column.items()))
117+
column_type = self.column_types[hash_key] if hash_key in self.column_types else self.column_type(column)
114118
if row[index] == None and ('timestamp' not in column_type or not column['default']):
115119
row[index] = '\N'
116120
elif row[index] == None and column['default']:
117121
row[index] = '1970-01-01 00:00:00'
118122
elif 'bit' in column_type:
119123
row[index] = bin(ord(row[index]))[2:]
120-
elif row[index].__class__ in (str, unicode):
124+
elif isinstance(row[index], (str, unicode, basestring)):
121125
if column_type == 'bytea':
122126
row[index] = Binary(row[index]).getquoted()[1:-8] if row[index] else row[index]
123127
elif 'text[' in column_type:
@@ -127,9 +131,9 @@ def process_row(self, table, row):
127131
elif column_type == 'boolean':
128132
# We got here because you used a tinyint(1), if you didn't want a bool, don't use that type
129133
row[index] = 't' if row[index] not in (None, 0) else 'f' if row[index] == 0 else row[index]
130-
elif row[index].__class__ in (date, datetime):
134+
elif isinstance(row[index], (date, datetime)):
131135
row[index] = row[index].isoformat()
132-
elif row[index].__class__ is timedelta:
136+
elif isinstance(row[index], timedelta):
133137
row[index] = datetime.utcfromtimestamp(row[index].total_seconds()).time().isoformat()
134138
else:
135139
row[index] = AsIs(row[index]).getquoted()

mysql2pgsql/lib/writer.py

Lines changed: 0 additions & 2 deletions
This file was deleted.

requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mysql-python
2+
psycopg2
3+
pyyaml
4+
termcolor
5+
argparse

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
'mysql-python>=1.2.3',
66
'psycopg2>=2.4.2',
77
'pyyaml>=3.10.0',
8+
'argparse',
89
]
910

1011
if os.name == 'posix':
1112
install_requires.append('termcolor>=1.1.0')
1213

1314
setup(
1415
name='py-mysql2pgsql',
15-
version='0.1.3',
16+
version='0.1.4',
1617
description='Tool for migrating/converting from mysql to postgresql.',
1718
long_description=open('README.rst').read(),
1819
license='MIT License',

tests/mysql2pgsql-test.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
# if a socket is specified we will use that
3+
# if tcp is chosen you can use compression
4+
mysql:
5+
hostname: 127.0.0.1
6+
port: 3306
7+
socket:
8+
username: root
9+
password:
10+
database: mysql2pgsql
11+
compress: false
12+
destination:
13+
# if file is given, output goes to file, else postgres
14+
file:
15+
postgres:
16+
hostname: 127.0.0.1
17+
port: 5432
18+
username: postgres
19+
password:
20+
database: mysql2pgsql
21+
22+
# if tables is given, only the listed tables will be converted. leave empty to convert all tables.
23+
#only_tables:
24+
#- table1
25+
#- table2
26+
# if exclude_tables is given, exclude the listed tables from the conversion.
27+
#exclude_tables:
28+
#- table3
29+
#- table4
30+
31+
# if supress_data is true, only the schema definition will be exported/migrated, and not the data
32+
supress_data: false
33+
34+
# if supress_ddl is true, only the data will be exported/imported, and not the schema
35+
supress_ddl: false
36+
37+
# if force_truncate is true, forces a table truncate before table loading
38+
force_truncate: false

0 commit comments

Comments
 (0)