Skip to content

Commit d02e0eb

Browse files
authored
docs: add dialect mapping (#13)
1 parent 3ed60b6 commit d02e0eb

2 files changed

Lines changed: 318 additions & 0 deletions

File tree

‎docs/dialect-mapping.rst‎

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
Dialect mapping
2+
===============
3+
4+
When ``db2sql`` reads a source database and emits SQL for a different target
5+
dialect, two things need to be translated:
6+
7+
1. **Column types** — the source column type is rewritten to the closest
8+
equivalent in the target dialect.
9+
2. **DEFAULT expressions** — built-in functions (``GETDATE()``, ``NEWID()``,
10+
``now()``, ``SYSDATE``, …) are rewritten to the target-dialect equivalent
11+
so the generated ``CREATE TABLE`` is replayable as-is.
12+
13+
Both are driven by static tables in the emitters; the tables below are the
14+
authoritative reference (extracted from
15+
``db2sql/infrastructure/emit/{postgres,mssql}/emitter.py``).
16+
17+
18+
Target: PostgreSQL
19+
------------------
20+
21+
Used when ``--target postgres`` (the default) is selected.
22+
23+
Type mapping
24+
~~~~~~~~~~~~
25+
26+
.. list-table::
27+
:header-rows: 1
28+
:widths: 30 30 40
29+
30+
* - Source type (lowercase)
31+
- Postgres type
32+
- Notes
33+
* - ``bit``, ``boolean``
34+
- ``boolean``
35+
- MSSQL ``bit`` becomes PG ``boolean``; literal ``0``/``1`` defaults are
36+
rewritten to ``FALSE``/``TRUE``.
37+
* - ``tinyint``, ``smallint``
38+
- ``smallint``
39+
-
40+
* - ``int``, ``integer``, ``mediumint``
41+
- ``integer``
42+
- Becomes ``serial`` when the column is an identity column.
43+
* - ``bigint``
44+
- ``bigint``
45+
- Becomes ``bigserial`` when the column is an identity column.
46+
* - ``real``, ``binary_float``
47+
- ``real``
48+
-
49+
* - ``float``, ``double``, ``binary_double``
50+
- ``double precision``
51+
-
52+
* - ``numeric``, ``decimal``, ``number``
53+
- ``numeric``
54+
- Precision/scale preserved when reported by the source.
55+
* - ``money``
56+
- ``numeric(19,4)``
57+
-
58+
* - ``smallmoney``
59+
- ``numeric(10,4)``
60+
-
61+
* - ``char``, ``nchar``
62+
- ``char``
63+
- Char length preserved.
64+
* - ``varchar``, ``varchar2``, ``nvarchar``, ``nvarchar2``
65+
- ``varchar``
66+
- Char length preserved.
67+
* - ``text``, ``ntext``, ``clob``, ``nclob``, ``long``, ``longtext``, ``mediumtext``
68+
- ``text``
69+
-
70+
* - ``binary``, ``varbinary``, ``blob``, ``bfile``, ``raw``, ``long raw``, ``image``
71+
- ``bytea``
72+
-
73+
* - ``date``
74+
- ``date``
75+
-
76+
* - ``time``
77+
- ``time``
78+
-
79+
* - ``datetime``, ``datetime2``, ``smalldatetime``, ``timestamp``
80+
- ``timestamp``
81+
-
82+
* - ``timestamp with time zone``, ``timestamp with local time zone``, ``datetimeoffset``
83+
- ``timestamptz``
84+
-
85+
* - ``uniqueidentifier``
86+
- ``uuid``
87+
-
88+
* - ``rowid``, ``urowid``
89+
- ``text``
90+
- Oracle pseudocolumns; rendered as opaque text.
91+
* - ``json``, ``jsonb``
92+
- ``jsonb``
93+
-
94+
* - ``xml``, ``xmltype``
95+
- ``xml``
96+
-
97+
98+
DEFAULT expression mapping
99+
~~~~~~~~~~~~~~~~~~~~~~~~~~
100+
101+
MSSQL wraps every default in extra parentheses (``((0))``, ``(getdate())``);
102+
those are stripped before matching. ``N'…'`` unicode-prefixed strings become
103+
plain ``'…'`` (Postgres strings are already Unicode).
104+
105+
.. list-table::
106+
:header-rows: 1
107+
:widths: 40 40 20
108+
109+
* - Source expression
110+
- Postgres equivalent
111+
- Origin
112+
* - ``GETDATE()``
113+
- ``now()``
114+
- MSSQL
115+
* - ``SYSDATETIME()``
116+
- ``LOCALTIMESTAMP``
117+
- MSSQL
118+
* - ``GETUTCDATE()``, ``SYSUTCDATETIME()``
119+
- ``(now() AT TIME ZONE 'utc')``
120+
- MSSQL
121+
* - ``SYSDATETIMEOFFSET()``
122+
- ``now()``
123+
- MSSQL
124+
* - ``SYSDATE``, ``SYSTIMESTAMP``
125+
- ``now()``
126+
- Oracle (bare keywords)
127+
* - ``NEWID()``, ``NEWSEQUENTIALID()``
128+
- ``gen_random_uuid()``
129+
- MSSQL
130+
* - ``SYS_GUID()``
131+
- ``gen_random_uuid()``
132+
- Oracle
133+
* - ``UUID()``
134+
- ``gen_random_uuid()``
135+
- MySQL
136+
* - ``SUSER_SNAME()``, ``SYSTEM_USER``, ``USER_NAME()``, ``USER``
137+
- ``CURRENT_USER``
138+
- MSSQL / Oracle
139+
* - ``DB_NAME()``
140+
- ``current_database()``
141+
- MSSQL
142+
* - ``0`` / ``1`` (on a ``boolean`` column)
143+
- ``FALSE`` / ``TRUE``
144+
- any
145+
* - ``b'0'`` / ``b'1'`` (on a ``boolean`` column)
146+
- ``FALSE`` / ``TRUE``
147+
- MySQL
148+
149+
Anything not in the table is passed through unchanged.
150+
151+
.. note::
152+
153+
MSSQL's ``INFORMATION_SCHEMA.COLUMNS`` reports ``CURRENT_TIMESTAMP`` and
154+
``GETDATE()`` defaults identically as ``(getdate())`` — they are
155+
indistinguishable at the metadata level. Both translate to ``now()`` in
156+
Postgres, which is semantically equivalent.
157+
158+
159+
Target: Microsoft SQL Server
160+
----------------------------
161+
162+
Used when ``--target mssql`` is selected.
163+
164+
Type mapping
165+
~~~~~~~~~~~~
166+
167+
.. list-table::
168+
:header-rows: 1
169+
:widths: 30 30 40
170+
171+
* - Source type (lowercase)
172+
- MSSQL type
173+
- Notes
174+
* - ``bit``, ``boolean``
175+
- ``bit``
176+
- PG ``boolean`` literals (``TRUE``/``FALSE``) become ``1``/``0``.
177+
* - ``tinyint``
178+
- ``tinyint``
179+
-
180+
* - ``smallint``
181+
- ``smallint``
182+
-
183+
* - ``int``, ``integer``, ``mediumint``
184+
- ``int``
185+
-
186+
* - ``bigint``
187+
- ``bigint``
188+
-
189+
* - ``real``, ``binary_float``
190+
- ``real``
191+
-
192+
* - ``float``, ``double``, ``double precision``, ``binary_double``
193+
- ``float``
194+
-
195+
* - ``numeric``, ``decimal``, ``number``
196+
- ``numeric``
197+
-
198+
* - ``money``
199+
- ``money``
200+
-
201+
* - ``smallmoney``
202+
- ``smallmoney``
203+
-
204+
* - ``char``, ``nchar``
205+
- ``nchar``
206+
- Unified to the Unicode variant; preserves length.
207+
* - ``varchar``, ``varchar2``, ``nvarchar``, ``nvarchar2``
208+
- ``nvarchar``
209+
- Same; length preserved.
210+
* - ``text``, ``ntext``, ``clob``, ``nclob``, ``long``, ``longtext``, ``mediumtext``
211+
- ``nvarchar(max)``
212+
-
213+
* - ``binary``, ``varbinary``
214+
- ``varbinary``
215+
-
216+
* - ``blob``, ``bfile``, ``raw``, ``long raw``, ``image``, ``bytea``
217+
- ``varbinary(max)``
218+
-
219+
* - ``date``
220+
- ``date``
221+
-
222+
* - ``time``
223+
- ``time``
224+
-
225+
* - ``datetime``, ``datetime2``, ``smalldatetime``, ``timestamp``
226+
- ``datetime2``
227+
- MSSQL's ``timestamp`` is a row-version; we never emit it for data.
228+
* - ``timestamp with time zone``, ``timestamp with local time zone``,
229+
``datetimeoffset``, ``timestamptz``
230+
- ``datetimeoffset``
231+
-
232+
* - ``uniqueidentifier``, ``uuid``
233+
- ``uniqueidentifier``
234+
-
235+
* - ``rowid``, ``urowid``, ``json``, ``jsonb``
236+
- ``nvarchar(max)``
237+
- MSSQL has no native JSON type.
238+
* - ``xml``, ``xmltype``
239+
- ``xml``
240+
-
241+
242+
DEFAULT expression mapping
243+
~~~~~~~~~~~~~~~~~~~~~~~~~~
244+
245+
PG ``literal::type`` casts (``'foo'::text``, ``0::integer``) are unwrapped to
246+
the literal. ANSI-compatible keywords (``CURRENT_USER``, ``SESSION_USER``,
247+
``SYSTEM_USER``, ``CURRENT_TIMESTAMP``) are left untouched.
248+
249+
.. list-table::
250+
:header-rows: 1
251+
:widths: 40 40 20
252+
253+
* - Source expression
254+
- MSSQL equivalent
255+
- Origin
256+
* - ``now()``, ``NOW()``
257+
- ``SYSDATETIME()``
258+
- PG / MySQL
259+
* - ``LOCALTIMESTAMP``
260+
- ``SYSDATETIME()``
261+
- PG (bare keyword)
262+
* - ``transaction_timestamp()``, ``statement_timestamp()``, ``clock_timestamp()``
263+
- ``SYSDATETIME()``
264+
- PG
265+
* - ``CURRENT_DATE``
266+
- ``CAST(SYSDATETIME() AS DATE)``
267+
- PG (bare keyword)
268+
* - ``CURRENT_TIME``
269+
- ``CAST(SYSDATETIME() AS TIME)``
270+
- PG (bare keyword)
271+
* - ``UTC_TIMESTAMP()``
272+
- ``SYSUTCDATETIME()``
273+
- MySQL
274+
* - ``SYSDATE``, ``SYSTIMESTAMP``
275+
- ``GETDATE()`` / ``SYSDATETIME()``
276+
- Oracle (bare keywords)
277+
* - ``gen_random_uuid()``, ``uuid_generate_v4()``
278+
- ``NEWID()``
279+
- PG
280+
* - ``SYS_GUID()``
281+
- ``NEWID()``
282+
- Oracle
283+
* - ``UUID()``
284+
- ``NEWID()``
285+
- MySQL
286+
* - ``current_database()``, ``current_catalog``
287+
- ``DB_NAME()``
288+
- PG
289+
* - ``current_schema``
290+
- ``SCHEMA_NAME()``
291+
- PG
292+
* - ``TRUE`` / ``FALSE`` (on a ``bit`` column)
293+
- ``1`` / ``0``
294+
- PG / any
295+
* - ``b'0'`` / ``b'1'`` (on a ``bit`` column)
296+
- ``0`` / ``1``
297+
- MySQL
298+
* - ``'foo'::text``, ``0::integer``
299+
- ``'foo'``, ``0``
300+
- PG cast stripped
301+
302+
Anything not in the table is passed through unchanged.
303+
304+
305+
Overriding the mapping
306+
----------------------
307+
308+
The type and default-value maps are static class attributes. To override a
309+
mapping for a specific column, the recommended approach is to write a small
310+
emitter subclass and register it as a plugin — see :doc:`plugins`.

‎docs/index.rst‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ changes directly to a live target database (migrate mode).
4242

4343
YAML/JSON configuration file: all keys, defaults, and examples.
4444

45+
.. grid-item-card:: :octicon:`arrow-switch` Dialect mapping
46+
:link: dialect-mapping
47+
:link-type: doc
48+
49+
How column types and ``DEFAULT`` expressions (``GETDATE()``,
50+
``NEWID()``, ``now()``, …) are translated between source and target.
51+
4552
.. grid-item-card:: :octicon:`code` API Reference
4653
:link: api/index
4754
:link-type: doc
@@ -100,6 +107,7 @@ Contents
100107
installation
101108
cli
102109
configuration
110+
dialect-mapping
103111
plugins
104112
api/index
105113
changelog

0 commit comments

Comments
 (0)