(text);
24 | }
25 | }
26 |
27 | #endregion
28 | }
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
⚠️ This project is still under development ⚠️
3 |
4 |
5 |
6 |
7 | # postgresql-to-mssql
8 |
9 | Migrate data from postgresql to sql server on the fly!
10 |
11 | ### Description
12 |
13 | This is a .NET console application that handles data migration (data from tables and views) from postgresql to sql server. It aims to make the process of migrating data from one database to another as seamless and easy as possible.
14 |
15 | ### Motivations
16 |
17 | As an open source enthusiast, I noticed the lack of workable open source tools for migrating data between databases, and the high cost of some paid tools. I wanted to make a contribution to the open source community by creating a tool that is both effective and accessible to all.
18 |
19 | ### Features
20 |
21 | - Migrates data from tables and views in PostgreSQL to SQL Server
22 | - Written in .NET for high performance and compatibility with a wide range of systems
23 | - Easy to use console interface
24 | - Open source and free to use
25 |
26 | ### Getting Started
27 |
28 | 1. Clone or download the repository
29 | 2. Open the solution in Visual Studio
30 | 3. Build the solution
31 | 4. Run the executable
32 | 5. Follow the prompts to configure the migration
33 |
34 | ### Contributing
35 |
36 | Please fork the repository and submit a pull request. For more [CONTRIBUTING.md](https://github.com/sanamhub/postgresql-to-mssql/blob/main/CONTRIBUTING.md)
37 |
38 | ### License
39 |
40 | This project is licensed under the [MIT License](https://github.com/sanamhub/postgresql-to-mssql/blob/main/LICENSE)
41 |
42 | ### Acknowledgements
43 |
44 | Thank you to the open source community for all of the support and resources that made this project possible.
45 |
46 | ### Support
47 |
48 | If you have any questions or issues, please open an discussion on GitHub or contact me directly.
49 |
50 | #### Happy migrating!
51 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | | Version | Supported |
6 | | ------- | ------------------ |
7 | | 1.0.x | :white_check_mark: |
8 | | 0.0.x | :x: |
9 |
10 | ## Reporting a Vulnerability
11 |
12 | Please file an issue if not exists.
13 |
--------------------------------------------------------------------------------
/Services/Interfaces/IService.cs:
--------------------------------------------------------------------------------
1 | namespace Application.Services.Interfaces;
2 |
3 | internal interface IService
4 | {
5 | void Migrate();
6 | }
7 |
--------------------------------------------------------------------------------
/Services/Service.cs:
--------------------------------------------------------------------------------
1 | using Application.Helpers;
2 | using Application.Providers.Interfaces;
3 | using Application.Services.Interfaces;
4 | using Application.Validators.Interfaces;
5 | using Dapper;
6 | using Microsoft.Data.SqlClient;
7 | using Spectre.Console;
8 | using System.Data;
9 |
10 | namespace Application.Services;
11 |
12 | internal class Service : IService
13 | {
14 | private readonly IProvider _provider;
15 | private readonly IValidator _validator;
16 |
17 | public Service(
18 | IProvider provider,
19 | IValidator validator
20 | )
21 | {
22 | _provider = provider;
23 | _validator = validator;
24 | }
25 |
26 | public void Migrate()
27 | {
28 | var errors = new List();
29 |
30 | try
31 | {
32 | SpectreConsoleHelper.WriteHeader("pgsql to mssql", Color.Blue);
33 |
34 | _validator.ValidateProviders();
35 |
36 | SpectreConsoleHelper.Log("Initializing...");
37 | AnsiConsole.Status()
38 | .Spinner(Spinner.Known.Arrow3)
39 | .SpinnerStyle(Style.Parse("green"))
40 | .Start("Starting the migration...", ctx =>
41 | {
42 | using var postgresConnection = _provider.GetPostgresqlConnection();
43 | using var sqlServerConnection = _provider.GetSqlServerConnection();
44 |
45 | postgresConnection.Open();
46 | sqlServerConnection.Open();
47 |
48 | ctx.Status("Fetching postgresql schemas");
49 | ctx.Spinner(Spinner.Known.BouncingBall);
50 | var getSchemasQuery = "SELECT schema_name FROM information_schema.schemata";
51 | var schemas = postgresConnection.Query(getSchemasQuery).ToList();
52 | SpectreConsoleHelper.Log("Fetched schemas from postgresql...");
53 |
54 | RemoveUnnecessarySchemas(schemas);
55 |
56 | ctx.Status("Looping through available schemas...");
57 | foreach (var sourceSchema in schemas)
58 | {
59 | string destinationSchema = $"{sourceSchema}_new";
60 |
61 | ctx.Status($"Creating {destinationSchema} schema in sql server...");
62 | var createDestinationSchemaQuery = $"CREATE SCHEMA [{destinationSchema}];";
63 | sqlServerConnection.Execute(createDestinationSchemaQuery);
64 | SpectreConsoleHelper.Log($"Created {destinationSchema} schema in sql server...");
65 |
66 | ctx.Status($"Fetching available tables from {sourceSchema} schema...");
67 | var getTablesQuery = $"SELECT table_name FROM information_schema.tables WHERE table_schema = '{sourceSchema}'";
68 | var tables = postgresConnection.Query(getTablesQuery).ToList();
69 | SpectreConsoleHelper.Log($"Fetched tables of {sourceSchema} schema from postgres");
70 |
71 | ctx.Status($"Looping through all tables of {sourceSchema} schema...");
72 | foreach (var table in tables)
73 | {
74 | ctx.Status($"Fetching column definition for {table} table...");
75 | var getColumnsQuery = $"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '{table}' AND table_schema = '{sourceSchema}'";
76 | var columns = postgresConnection.Query(getColumnsQuery);
77 | SpectreConsoleHelper.Log($"Fetched column definition for {table} table...");
78 |
79 | ctx.Status($"Creating table {destinationSchema}.{table} in sql server...");
80 | var createTableQuery = $"CREATE TABLE {destinationSchema}.{table} (";
81 | createTableQuery += string.Join(", ", columns.Select(column => $"[{column.column_name}] {ConvertPostgreSqlToSqlServerDataType(column.data_type)}"));
82 | createTableQuery += ")";
83 | sqlServerConnection.Execute(createTableQuery);
84 | SpectreConsoleHelper.Log($"Created table {destinationSchema}.{table} in sql server...");
85 |
86 | IDataReader data;
87 | try
88 | {
89 | ctx.Status($"Fetching data from {sourceSchema}.{table} from postgresql...");
90 | data = postgresConnection.ExecuteReader($"SELECT * FROM {sourceSchema}.{table}");
91 | SpectreConsoleHelper.Log($"Fetched data from {sourceSchema}.{table} table of postgresql...");
92 |
93 | ctx.Status("Coverting the data into proper shape before migrating to sql server...");
94 | var dataTable = new DataTable();
95 | dataTable.Load(data);
96 | SpectreConsoleHelper.Log("Converted data into proper shape...");
97 |
98 | ctx.Status($"Transferring data from [blue]{sourceSchema}.{table}[/] to [green]{destinationSchema}.{table}[/]");
99 | using var bulkCopy = new SqlBulkCopy(sqlServerConnection);
100 | bulkCopy.DestinationTableName = $"{destinationSchema}.{table}";
101 | bulkCopy.BulkCopyTimeout = 300;
102 | bulkCopy.WriteToServer(dataTable);
103 | SpectreConsoleHelper.Success($"Successfully transferred data from {sourceSchema}.{table} to {destinationSchema}.{table}");
104 | }
105 | catch (Exception ex)
106 | {
107 | errors.Add($"{sourceSchema}~{table}");
108 | AnsiConsole.WriteException(ex);
109 | }
110 | }
111 | }
112 | });
113 | SpectreConsoleHelper.WriteHeader("Success!", Color.Green);
114 | }
115 | catch (Exception ex)
116 | {
117 | AnsiConsole.WriteException(ex);
118 | }
119 | finally
120 | {
121 | if (errors.Count != 0)
122 | {
123 | var table = new Table();
124 | table.Title("List of failed migration table/views");
125 |
126 | table.AddColumn("SourceSchema");
127 | table.AddColumn("SourceTable");
128 |
129 | foreach (var error in errors)
130 | {
131 | var errorDetails = error.Split("~");
132 | table.AddRow(errorDetails[0], errorDetails[1]);
133 | }
134 |
135 | table.Border(TableBorder.Rounded);
136 | AnsiConsole.Write(table);
137 | }
138 | }
139 | }
140 |
141 | #region Private methods
142 |
143 | private static void RemoveUnnecessarySchemas(List schemas)
144 | {
145 | schemas.Remove("information_schema");
146 | schemas.Remove("pg_catalog");
147 | schemas.Remove("pg_toast");
148 | schemas.Remove("pg_temp_1");
149 | schemas.Remove("pg_toast_temp_1");
150 | }
151 |
152 | private static string ConvertPostgreSqlToSqlServerDataType(string postgresDataType)
153 | {
154 | var map = new Dictionary
155 | {
156 | { "bigint", "bigint" },
157 | { "boolean", "bit" },
158 | { "character", "char" },
159 | { "character varying", "nvarchar(max)" },
160 | { "date", "date" },
161 | { "double precision", "float" },
162 | { "integer", "int" },
163 | { "interval", "time" },
164 | { "numeric", "decimal" },
165 | { "real", "real" },
166 | { "smallint", "smallint" },
167 | { "text", "nvarchar(max)" },
168 | { "time", "time" },
169 | { "timestamp", "datetime2" },
170 | { "timestamptz", "datetimeoffset" },
171 | { "uuid", "uniqueidentifier" },
172 | { "bytea", "varbinary(max)" },
173 | { "bit", "bit" },
174 | { "bit varying", "varbinary(max)" },
175 | { "money", "money" },
176 | { "json", "nvarchar(max)" },
177 | { "jsonb", "nvarchar(max)" },
178 | { "cidr", "nvarchar(max)" },
179 | { "inet", "nvarchar(max)" },
180 | { "macaddr", "nvarchar(max)" },
181 | { "tsvector", "nvarchar(max)" },
182 | { "tsquery", "nvarchar(max)" },
183 | { "array", "nvarchar(max)" },
184 | { "domain", "nvarchar(max)" },
185 | { "timestamp with time zone", "datetimeoffset" },
186 | };
187 |
188 | return map.TryGetValue(postgresDataType.ToLower(), out string? value) ? value.ToUpper() : "nvarchar(max)".ToUpper();
189 | }
190 |
191 | #endregion
192 | }
193 |
--------------------------------------------------------------------------------
/Validators/Interfaces/IValidator.cs:
--------------------------------------------------------------------------------
1 | namespace Application.Validators.Interfaces;
2 |
3 | internal interface IValidator
4 | {
5 | void ValidateProviders();
6 | }
7 |
--------------------------------------------------------------------------------
/Validators/Validator.cs:
--------------------------------------------------------------------------------
1 | using Application.Helpers;
2 | using Application.Providers.Interfaces;
3 | using Application.Validators.Interfaces;
4 | using System.Data;
5 |
6 | namespace Application.Validators;
7 |
8 | internal class Validator : IValidator
9 | {
10 | private readonly IProvider _provider;
11 |
12 | public Validator(IProvider provider)
13 | {
14 | _provider = provider;
15 | }
16 |
17 | public void ValidateProviders()
18 | {
19 | using (var sqlServerConnection = _provider.GetSqlServerConnection())
20 | {
21 | if (!IsServerConnected(sqlServerConnection))
22 | {
23 | SpectreConsoleHelper.Error("Invalid sql server connection.. ( Configure properly at connectionString.json )");
24 | Console.ReadLine();
25 | }
26 | }
27 | SpectreConsoleHelper.Success("Sql server connected...");
28 |
29 | using (var postgreSqlConnection = _provider.GetPostgresqlConnection())
30 | {
31 | if (!IsServerConnected(postgreSqlConnection))
32 | {
33 | SpectreConsoleHelper.Error("Invalid postgresql connection.. ( Configure properly at connectionString.json )");
34 | Console.ReadLine();
35 | }
36 | }
37 | SpectreConsoleHelper.Success("PostgreSQL connected...");
38 | }
39 |
40 | #region Private methods
41 |
42 | private static bool IsServerConnected(IDbConnection connection)
43 | {
44 | try
45 | {
46 | connection.Open();
47 | return true;
48 | }
49 | catch (Exception ex)
50 | {
51 | SpectreConsoleHelper.Error(ex.Message);
52 | return false;
53 | }
54 | }
55 |
56 | #endregion
57 | }
58 |
--------------------------------------------------------------------------------
/connectionString.json:
--------------------------------------------------------------------------------
1 | {
2 | "SqlServerConnectionString": "Server=localhost;Database=lms;User Id=sa;Password=1;TrustServerCertificate=True;",
3 | "PostgreSqlConnectionString": "Server=127.0.0.1;Port=5432;Database=lms;User Id=postgres;Password=postgres;"
4 | }
5 |
--------------------------------------------------------------------------------