meerschaum.utils.dtypes.sql

Utility functions for working with SQL data types.

   1#! /usr/bin/env python3
   2# -*- coding: utf-8 -*-
   3# vim:fenc=utf-8
   4
   5"""
   6Utility functions for working with SQL data types.
   7"""
   8
   9from __future__ import annotations
  10from meerschaum.utils.typing import Dict, Union, Tuple, Optional
  11from meerschaum._internal.static import STATIC_CONFIG as _STATIC_CONFIG
  12from meerschaum.utils.dtypes import MRSM_PRECISION_UNITS_ABBREVIATIONS as _MRSM_PRECISION_UNITS_ABBREVIATIONS
  13
  14NUMERIC_PRECISION_FLAVORS: Dict[str, Tuple[int, int]] = {
  15    'mariadb': (38, 20),
  16    'mysql': (38, 20),
  17    'mssql': (28, 10),
  18}
  19NUMERIC_AS_TEXT_FLAVORS = {'sqlite', 'duckdb', 'geopackage'}
  20TIMEZONE_NAIVE_FLAVORS = {'oracle', 'mysql', 'mariadb'}
  21
  22### MySQL doesn't allow for casting as BIGINT, so this is a workaround.
  23DB_FLAVORS_CAST_DTYPES = {
  24    'mariadb': {
  25        'BIGINT': 'DECIMAL',
  26        'TINYINT': 'SIGNED INT',
  27        'TEXT': 'CHAR(10000) CHARACTER SET utf8',
  28        'BOOL': 'SIGNED INT',
  29        'BOOLEAN': 'SIGNED INT',
  30        'DOUBLE PRECISION': 'DECIMAL',
  31        'DOUBLE': 'DECIMAL',
  32        'FLOAT': 'DECIMAL',
  33    },
  34    'mysql': {
  35        'BIGINT': 'DECIMAL',
  36        'TINYINT': 'SIGNED INT',
  37        'TEXT': 'CHAR(10000) CHARACTER SET utf8',
  38        'INT': 'SIGNED INT',
  39        'INTEGER': 'SIGNED INT',
  40        'BOOL': 'SIGNED INT',
  41        'BOOLEAN': 'SIGNED INT',
  42        'DOUBLE PRECISION': 'DECIMAL',
  43        'DOUBLE': 'DECIMAL',
  44        'FLOAT': 'DECIMAL',
  45    },
  46    'sqlite': {
  47        'BOOLEAN': 'INTEGER',
  48        'REAL': 'FLOAT',
  49    },
  50    'geopackage': {
  51        'BOOLEAN': 'INTEGER',
  52        'REAL': 'FLOAT',
  53    },
  54    'oracle': {
  55        'NVARCHAR(2000)': 'NVARCHAR2(2000)',
  56        'NVARCHAR': 'NVARCHAR2(2000)',
  57        'NVARCHAR2': 'NVARCHAR2(2000)',
  58        'CHAR': 'CHAR(36)',  # UUID columns
  59    },
  60    'mssql': {
  61        'NVARCHAR COLLATE "SQL Latin1 General CP1 CI AS"': 'NVARCHAR(MAX)',
  62        'NVARCHAR COLLATE "SQL_Latin1_General_CP1_CI_AS"': 'NVARCHAR(MAX)',
  63        'VARCHAR COLLATE "SQL Latin1 General CP1 CI AS"': 'NVARCHAR(MAX)',
  64        'VARCHAR COLLATE "SQL_Latin1_General_CP1_CI_AS"': 'NVARCHAR(MAX)',
  65        'NVARCHAR': 'NVARCHAR(MAX)',
  66    },
  67}
  68for _flavor, (_precision, _scale) in NUMERIC_PRECISION_FLAVORS.items():
  69    if _flavor not in DB_FLAVORS_CAST_DTYPES:
  70        DB_FLAVORS_CAST_DTYPES[_flavor] = {}
  71    DB_FLAVORS_CAST_DTYPES[_flavor].update({
  72        'NUMERIC': f"NUMERIC({_precision}, {_scale})",
  73        'DECIMAL': f"DECIMAL({_precision}, {_scale})",
  74    })
  75
  76_default_precision_unit = _STATIC_CONFIG['dtypes']['datetime']['default_precision_unit']
  77_default_precision_abbreviation = _MRSM_PRECISION_UNITS_ABBREVIATIONS[_default_precision_unit]
  78
  79DB_TO_PD_DTYPES: Dict[str, Union[str, Dict[str, str]]] = {
  80    'FLOAT': 'float64[pyarrow]',
  81    'REAL': 'float64[pyarrow]',
  82    'DOUBLE_PRECISION': 'float64[pyarrow]',
  83    'DOUBLE': 'float64[pyarrow]',
  84    'DECIMAL': 'numeric',
  85    'BIGINT': 'int64[pyarrow]',
  86    'INT': 'int32[pyarrow]',
  87    'INTEGER': 'int32[pyarrow]',
  88    'NUMBER': 'numeric',
  89    'NUMERIC': 'numeric',
  90    'GEOMETRY': 'geometry',
  91    'GEOMETRY(GEOMETRY)': 'geometry',
  92    'TIMESTAMP': f'datetime64[{_default_precision_abbreviation}]',
  93    'TIMESTAMP WITHOUT TIMEZONE': f'datetime64[{_default_precision_abbreviation}]',
  94    'TIMESTAMP WITH TIMEZONE': f'datetime64[{_default_precision_abbreviation}, UTC]',
  95    'TIMESTAMP WITH TIME ZONE': f'datetime64[{_default_precision_abbreviation}, UTC]',
  96    'TIMESTAMPTZ': f'datetime64[{_default_precision_abbreviation}, UTC]',
  97    'DATE': 'date',
  98    'DATETIME': f'datetime64[{_default_precision_abbreviation}]',
  99    'DATETIME2': 'datetime64[us]',
 100    'DATETIME2(6)': 'datetime64[us]',
 101    'DATETIME2(3)': 'datetime64[ms]',
 102    'DATETIME2(0)': 'datetime64[s]',
 103    'DATETIMEOFFSET': 'datetime64[us, UTC]',
 104    'DATETIMEOFFSET(6)': 'datetime64[us, UTC]',
 105    'DATETIMEOFFSET(3)': 'datetime64[ms, UTC]',
 106    'DATETIMEOFFSET(0)': 'datetime64[s, UTC]',
 107    'TEXT': 'string[pyarrow]',
 108    'VARCHAR': 'string[pyarrow]',
 109    'CLOB': 'string[pyarrow]',
 110    'BOOL': 'bool[pyarrow]',
 111    'BOOLEAN': 'bool[pyarrow]',
 112    'BOOLEAN()': 'bool[pyarrow]',
 113    'TINYINT': 'bool[pyarrow]',
 114    'TINYINT(1)': 'bool[pyarrow]',
 115    'BIT': 'bool[pyarrow]',
 116    'BIT(1)': 'bool[pyarrow]',
 117    'JSON': 'json',
 118    'JSONB': 'json',
 119    'UUID': 'uuid',
 120    'UNIQUEIDENTIFIER': 'uuid',
 121    'BYTEA': 'bytes',
 122    'BLOB': 'bytes',
 123    'VARBINARY': 'bytes',
 124    'VARBINARY(MAX)': 'bytes',
 125    'substrings': {
 126        'CHAR': 'string[pyarrow]',
 127        'TIMESTAMP': f'datetime64[{_default_precision_abbreviation}]',
 128        'TIME': f'datetime64[{_default_precision_abbreviation}]',
 129        'DATE': 'date',
 130        'DOUBLE': 'double[pyarrow]',
 131        'DECIMAL': 'numeric',
 132        'NUMERIC': 'numeric',
 133        'NUMBER': 'numeric',
 134        'INT': 'int64[pyarrow]',
 135        'BOOL': 'bool[pyarrow]',
 136        'JSON': 'json',
 137        'BYTE': 'bytes',
 138        'LOB': 'bytes',
 139        'BINARY': 'bytes',
 140        'GEOMETRY': 'geometry',
 141        'GEOGRAPHY': 'geography',
 142    },
 143    'default': 'object',
 144}
 145### Map pandas dtypes to flavor-specific dtypes.
 146PD_TO_DB_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {
 147    'int': {
 148        'timescaledb': 'BIGINT',
 149        'timescaledb-ha': 'BIGINT',
 150        'postgresql': 'BIGINT',
 151        'postgis': 'BIGINT',
 152        'mariadb': 'BIGINT',
 153        'mysql': 'BIGINT',
 154        'mssql': 'BIGINT',
 155        'oracle': 'INT',
 156        'sqlite': 'BIGINT',
 157        'geopackage': 'BIGINT',
 158        'duckdb': 'BIGINT',
 159        'citus': 'BIGINT',
 160        'cockroachdb': 'BIGINT',
 161        'default': 'INT',
 162    },
 163    'uint': {
 164        'timescaledb': 'BIGINT',
 165        'timescaledb-ha': 'BIGINT',
 166        'postgresql': 'BIGINT',
 167        'postgis': 'BIGINT',
 168        'mariadb': 'BIGINT',
 169        'mysql': 'BIGINT',
 170        'mssql': 'BIGINT',
 171        'oracle': 'INT',
 172        'sqlite': 'BIGINT',
 173        'geopackage': 'BIGINT',
 174        'duckdb': 'BIGINT',
 175        'citus': 'BIGINT',
 176        'cockroachdb': 'BIGINT',
 177        'default': 'INT',
 178    },
 179    'int8': {
 180        'timescaledb': 'SMALLINT',
 181        'timescaledb-ha': 'SMALLINT',
 182        'postgresql': 'SMALLINT',
 183        'postgis': 'SMALLINT',
 184        'mariadb': 'SMALLINT',
 185        'mysql': 'SMALLINT',
 186        'mssql': 'SMALLINT',
 187        'oracle': 'INT',
 188        'sqlite': 'INT',
 189        'geopackage': 'INT',
 190        'duckdb': 'SMALLINT',
 191        'citus': 'SMALLINT',
 192        'cockroachdb': 'SMALLINT',
 193        'default': 'INT',
 194    },
 195    'uint8': {
 196        'timescaledb': 'SMALLINT',
 197        'timescaledb-ha': 'SMALLINT',
 198        'postgresql': 'SMALLINT',
 199        'postgis': 'SMALLINT',
 200        'mariadb': 'SMALLINT',
 201        'mysql': 'SMALLINT',
 202        'mssql': 'SMALLINT',
 203        'oracle': 'INT',
 204        'sqlite': 'INT',
 205        'geopackage': 'INT',
 206        'duckdb': 'SMALLINT',
 207        'citus': 'SMALLINT',
 208        'cockroachdb': 'SMALLINT',
 209        'default': 'INT',
 210    },
 211    'int16': {
 212        'timescaledb': 'SMALLINT',
 213        'timescaledb-ha': 'SMALLINT',
 214        'postgresql': 'SMALLINT',
 215        'postgis': 'SMALLINT',
 216        'mariadb': 'SMALLINT',
 217        'mysql': 'SMALLINT',
 218        'mssql': 'SMALLINT',
 219        'oracle': 'INT',
 220        'sqlite': 'INT',
 221        'geopackage': 'INT',
 222        'duckdb': 'SMALLINT',
 223        'citus': 'SMALLINT',
 224        'cockroachdb': 'SMALLINT',
 225        'default': 'INT',
 226    },
 227    'int32': {
 228        'timescaledb': 'INT',
 229        'timescaledb-ha': 'INT',
 230        'postgresql': 'INT',
 231        'postgis': 'INT',
 232        'mariadb': 'INT',
 233        'mysql': 'INT',
 234        'mssql': 'INT',
 235        'oracle': 'INT',
 236        'sqlite': 'INT',
 237        'geopackage': 'INT',
 238        'duckdb': 'INT',
 239        'citus': 'INT',
 240        'cockroachdb': 'INT',
 241        'default': 'INT',
 242    },
 243    'int64': {
 244        'timescaledb': 'BIGINT',
 245        'timescaledb-ha': 'BIGINT',
 246        'postgresql': 'BIGINT',
 247        'postgis': 'BIGINT',
 248        'mariadb': 'BIGINT',
 249        'mysql': 'BIGINT',
 250        'mssql': 'BIGINT',
 251        'oracle': 'INT',
 252        'sqlite': 'BIGINT',
 253        'geopackage': 'BIGINT',
 254        'duckdb': 'BIGINT',
 255        'citus': 'BIGINT',
 256        'cockroachdb': 'BIGINT',
 257        'default': 'INT',
 258    },
 259    'float': {
 260        'timescaledb': 'DOUBLE PRECISION',
 261        'timescaledb-ha': 'DOUBLE PRECISION',
 262        'postgresql': 'DOUBLE PRECISION',
 263        'postgis': 'DOUBLE PRECISION',
 264        'mariadb': 'DOUBLE PRECISION',
 265        'mysql': 'DOUBLE PRECISION',
 266        'mssql': 'FLOAT',
 267        'oracle': 'FLOAT',
 268        'sqlite': 'FLOAT',
 269        'geopackage': 'FLOAT',
 270        'duckdb': 'DOUBLE PRECISION',
 271        'citus': 'DOUBLE PRECISION',
 272        'cockroachdb': 'DOUBLE PRECISION',
 273        'default': 'DOUBLE',
 274    },
 275    'double': {
 276        'timescaledb': 'DOUBLE PRECISION',
 277        'timescaledb-ha': 'DOUBLE PRECISION',
 278        'postgresql': 'DOUBLE PRECISION',
 279        'postgis': 'DOUBLE PRECISION',
 280        'mariadb': 'DOUBLE PRECISION',
 281        'mysql': 'DOUBLE PRECISION',
 282        'mssql': 'FLOAT',
 283        'oracle': 'FLOAT',
 284        'sqlite': 'FLOAT',
 285        'geopackage': 'FLOAT',
 286        'duckdb': 'DOUBLE PRECISION',
 287        'citus': 'DOUBLE PRECISION',
 288        'cockroachdb': 'DOUBLE PRECISION',
 289        'default': 'DOUBLE',
 290    },
 291    'datetime64[ns]': {
 292        'timescaledb': 'TIMESTAMP(6)',
 293        'timescaledb-ha': 'TIMESTAMP(6)',
 294        'postgresql': 'TIMESTAMP(6)',
 295        'postgis': 'TIMESTAMP(6)',
 296        'mariadb': 'DATETIME',
 297        'mysql': 'DATETIME',
 298        'mssql': 'DATETIME2(7)',
 299        'oracle': 'TIMESTAMP(9)',
 300        'sqlite': 'DATETIME',
 301        'geopackage': 'DATETIME',
 302        'duckdb': 'TIMESTAMP(6)',
 303        'citus': 'TIMESTAMP(6)',
 304        'cockroachdb': 'TIMESTAMP(6)',
 305        'default': 'DATETIME',
 306    },
 307    'datetime64[ns, UTC]': {
 308        'timescaledb': 'TIMESTAMPTZ(6)',
 309        'timescaledb-ha': 'TIMESTAMPTZ(6)',
 310        'postgresql': 'TIMESTAMPTZ(9)',
 311        'postgis': 'TIMESTAMPTZ(6)',
 312        'mariadb': 'DATETIME',
 313        'mysql': 'DATETIME',
 314        'mssql': 'DATETIMEOFFSET(7)',
 315        'oracle': 'TIMESTAMP(9)',
 316        'sqlite': 'TIMESTAMP',
 317        'geopackage': 'TIMESTAMP',
 318        'duckdb': 'TIMESTAMPTZ',
 319        'citus': 'TIMESTAMPTZ(6)',
 320        'cockroachdb': 'TIMESTAMPTZ(6)',
 321        'default': 'TIMESTAMPTZ',
 322    },
 323    'datetime64[us]': {
 324        'timescaledb': 'TIMESTAMP(6)',
 325        'timescaledb-ha': 'TIMESTAMP(6)',
 326        'postgresql': 'TIMESTAMP(6)',
 327        'postgis': 'TIMESTAMP(6)',
 328        'mariadb': 'DATETIME',
 329        'mysql': 'DATETIME',
 330        'mssql': 'DATETIME2(6)',
 331        'oracle': 'TIMESTAMP(6)',
 332        'sqlite': 'DATETIME',
 333        'geopackage': 'DATETIME',
 334        'duckdb': 'TIMESTAMP(6)',
 335        'citus': 'TIMESTAMP(6)',
 336        'cockroachdb': 'TIMESTAMP',
 337        'default': 'DATETIME',
 338    },
 339    'datetime64[us, UTC]': {
 340        'timescaledb': 'TIMESTAMPTZ(6)',
 341        'timescaledb-ha': 'TIMESTAMPTZ(6)',
 342        'postgresql': 'TIMESTAMPTZ(6)',
 343        'postgis': 'TIMESTAMPTZ(6)',
 344        'mariadb': 'DATETIME',
 345        'mysql': 'DATETIME',
 346        'mssql': 'DATETIMEOFFSET(6)',
 347        'oracle': 'TIMESTAMP(6)',
 348        'sqlite': 'TIMESTAMP',
 349        'geopackage': 'TIMESTAMP',
 350        'duckdb': 'TIMESTAMPTZ',
 351        'citus': 'TIMESTAMPTZ(6)',
 352        'cockroachdb': 'TIMESTAMPTZ(6)',
 353        'default': 'TIMESTAMPTZ',
 354    },
 355    'datetime64[ms]': {
 356        'timescaledb': 'TIMESTAMP(3)',
 357        'timescaledb-ha': 'TIMESTAMP(3)',
 358        'postgresql': 'TIMESTAMP(3)',
 359        'postgis': 'TIMESTAMP(3)',
 360        'mariadb': 'DATETIME',
 361        'mysql': 'DATETIME',
 362        'mssql': 'DATETIME2(3)',
 363        'oracle': 'TIMESTAMP(3)',
 364        'sqlite': 'DATETIME',
 365        'geopackage': 'DATETIME',
 366        'duckdb': 'TIMESTAMP(3)',
 367        'citus': 'TIMESTAMP(3)',
 368        'cockroachdb': 'TIMESTAMP(3)',
 369        'default': 'DATETIME',
 370    },
 371    'datetime64[ms, UTC]': {
 372        'timescaledb': 'TIMESTAMPTZ(3)',
 373        'timescaledb-ha': 'TIMESTAMPTZ(3)',
 374        'postgresql': 'TIMESTAMPTZ(3)',
 375        'postgis': 'TIMESTAMPTZ(3)',
 376        'mariadb': 'DATETIME',
 377        'mysql': 'DATETIME',
 378        'mssql': 'DATETIMEOFFSET(3)',
 379        'oracle': 'TIMESTAMP(3)',
 380        'sqlite': 'TIMESTAMP',
 381        'geopackage': 'TIMESTAMP',
 382        'duckdb': 'TIMESTAMPTZ',
 383        'citus': 'TIMESTAMPTZ(3)',
 384        'cockroachdb': 'TIMESTAMPTZ(3)',
 385        'default': 'TIMESTAMPTZ',
 386    },
 387    'datetime64[s]': {
 388        'timescaledb': 'TIMESTAMP(0)',
 389        'timescaledb-ha': 'TIMESTAMP(0)',
 390        'postgresql': 'TIMESTAMP(0)',
 391        'postgis': 'TIMESTAMP(0)',
 392        'mariadb': 'DATETIME',
 393        'mysql': 'DATETIME',
 394        'mssql': 'DATETIME2(0)',
 395        'oracle': 'TIMESTAMP(0)',
 396        'sqlite': 'DATETIME',
 397        'geopackage': 'DATETIME',
 398        'duckdb': 'TIMESTAMP(0)',
 399        'citus': 'TIMESTAMP(0)',
 400        'cockroachdb': 'TIMESTAMP(0)',
 401        'default': 'DATETIME',
 402    },
 403    'datetime64[s, UTC]': {
 404        'timescaledb': 'TIMESTAMPTZ(0)',
 405        'timescaledb-ha': 'TIMESTAMPTZ(0)',
 406        'postgresql': 'TIMESTAMPTZ(0)',
 407        'postgis': 'TIMESTAMPTZ(0)',
 408        'mariadb': 'DATETIME',
 409        'mysql': 'DATETIME',
 410        'mssql': 'DATETIMEOFFSET(0)',
 411        'oracle': 'TIMESTAMP(0)',
 412        'sqlite': 'TIMESTAMP',
 413        'geopackage': 'TIMESTAMP',
 414        'duckdb': 'TIMESTAMPTZ',
 415        'citus': 'TIMESTAMPTZ(0)',
 416        'cockroachdb': 'TIMESTAMPTZ(0)',
 417        'default': 'TIMESTAMPTZ(0)',
 418    },
 419    'datetime': {
 420        'timescaledb': 'TIMESTAMPTZ',
 421        'timescaledb-ha': 'TIMESTAMPTZ',
 422        'postgresql': 'TIMESTAMPTZ',
 423        'postgis': 'TIMESTAMPTZ',
 424        'mariadb': 'DATETIME',
 425        'mysql': 'DATETIME',
 426        'mssql': 'DATETIMEOFFSET',
 427        'oracle': 'TIMESTAMP',
 428        'sqlite': 'TIMESTAMP',
 429        'geopackage': 'TIMESTAMP',
 430        'duckdb': 'TIMESTAMPTZ',
 431        'citus': 'TIMESTAMPTZ',
 432        'cockroachdb': 'TIMESTAMPTZ',
 433        'default': 'TIMESTAMPTZ',
 434    },
 435    'datetimetz': {
 436        'timescaledb': 'TIMESTAMPTZ',
 437        'timescaledb-ha': 'TIMESTAMPTZ',
 438        'postgresql': 'TIMESTAMPTZ',
 439        'postgis': 'TIMESTAMPTZ',
 440        'mariadb': 'DATETIME',
 441        'mysql': 'DATETIME',
 442        'mssql': 'DATETIMEOFFSET',
 443        'oracle': 'TIMESTAMP',
 444        'sqlite': 'TIMESTAMP',
 445        'geopackage': 'TIMESTAMP',
 446        'duckdb': 'TIMESTAMPTZ',
 447        'citus': 'TIMESTAMPTZ',
 448        'cockroachdb': 'TIMESTAMPTZ',
 449        'default': 'TIMESTAMPTZ',
 450    },
 451    'date': {
 452        'timescaledb': 'DATE',
 453        'timescaledb-ha': 'DATE',
 454        'postgresql': 'DATE',
 455        'postgis': 'DATE',
 456        'mariadb': 'DATE',
 457        'mysql': 'DATE',
 458        'mssql': 'DATE',
 459        'oracle': 'DATE',
 460        'sqlite': 'DATE',
 461        'geopackage': 'DATE',
 462        'duckdb': 'DATE',
 463        'citus': 'DATE',
 464        'cockroachdb': 'DATE',
 465        'default': 'DATE',
 466    },
 467    'bool': {
 468        'timescaledb': 'BOOLEAN',
 469        'timescaledb-ha': 'BOOLEAN',
 470        'postgresql': 'BOOLEAN',
 471        'postgis': 'BOOLEAN',
 472        'mariadb': 'BOOLEAN',
 473        'mysql': 'BOOLEAN',
 474        'mssql': 'BIT',
 475        'oracle': 'INTEGER',
 476        'sqlite': 'FLOAT',
 477        'geopackage': 'FLOAT',
 478        'duckdb': 'BOOLEAN',
 479        'citus': 'BOOLEAN',
 480        'cockroachdb': 'BOOLEAN',
 481        'default': 'BOOLEAN',
 482    },
 483    'bool[pyarrow]': {
 484        'timescaledb': 'BOOLEAN',
 485        'timescaledb-ha': 'BOOLEAN',
 486        'postgresql': 'BOOLEAN',
 487        'postgis': 'BOOLEAN',
 488        'mariadb': 'BOOLEAN',
 489        'mysql': 'BOOLEAN',
 490        'mssql': 'BIT',
 491        'oracle': 'INTEGER',
 492        'sqlite': 'FLOAT',
 493        'geopackage': 'FLOAT',
 494        'duckdb': 'BOOLEAN',
 495        'citus': 'BOOLEAN',
 496        'cockroachdb': 'BOOLEAN',
 497        'default': 'BOOLEAN',
 498    },
 499    'boolean': {
 500        'timescaledb': 'BOOLEAN',
 501        'timescaledb-ha': 'BOOLEAN',
 502        'postgresql': 'BOOLEAN',
 503        'postgis': 'BOOLEAN',
 504        'mariadb': 'BOOLEAN',
 505        'mysql': 'BOOLEAN',
 506        'mssql': 'BIT',
 507        'oracle': 'INTEGER',
 508        'sqlite': 'FLOAT',
 509        'geopackage': 'FLOAT',
 510        'duckdb': 'BOOLEAN',
 511        'citus': 'BOOLEAN',
 512        'cockroachdb': 'BOOLEAN',
 513        'default': 'BOOLEAN',
 514    },
 515    'object': {
 516        'timescaledb': 'TEXT',
 517        'timescaledb-ha': 'TEXT',
 518        'postgresql': 'TEXT',
 519        'postgis': 'TEXT',
 520        'mariadb': 'TEXT',
 521        'mysql': 'TEXT',
 522        'mssql': 'NVARCHAR(MAX)',
 523        'oracle': 'NVARCHAR2(2000)',
 524        'sqlite': 'TEXT',
 525        'geopackage': 'TEXT',
 526        'duckdb': 'TEXT',
 527        'citus': 'TEXT',
 528        'cockroachdb': 'TEXT',
 529        'default': 'TEXT',
 530    },
 531    'string': {
 532        'timescaledb': 'TEXT',
 533        'timescaledb-ha': 'TEXT',
 534        'postgresql': 'TEXT',
 535        'postgis': 'TEXT',
 536        'mariadb': 'TEXT',
 537        'mysql': 'TEXT',
 538        'mssql': 'NVARCHAR(MAX)',
 539        'oracle': 'NVARCHAR2(2000)',
 540        'sqlite': 'TEXT',
 541        'geopackage': 'TEXT',
 542        'duckdb': 'TEXT',
 543        'citus': 'TEXT',
 544        'cockroachdb': 'TEXT',
 545        'default': 'TEXT',
 546    },
 547    'unicode': {
 548        'timescaledb': 'TEXT',
 549        'timescaledb-ha': 'TEXT',
 550        'postgresql': 'TEXT',
 551        'postgis': 'TEXT',
 552        'mariadb': 'TEXT',
 553        'mysql': 'TEXT',
 554        'mssql': 'NVARCHAR(MAX)',
 555        'oracle': 'NVARCHAR2(2000)',
 556        'sqlite': 'TEXT',
 557        'geopackage': 'TEXT',
 558        'duckdb': 'TEXT',
 559        'citus': 'TEXT',
 560        'cockroachdb': 'TEXT',
 561        'default': 'TEXT',
 562    },
 563    'json': {
 564        'timescaledb': 'JSONB',
 565        'timescaledb-ha': 'JSONB',
 566        'postgresql': 'JSONB',
 567        'postgis': 'JSONB',
 568        'mariadb': 'TEXT',
 569        'mysql': 'TEXT',
 570        'mssql': 'NVARCHAR(MAX)',
 571        'oracle': 'NVARCHAR2(2000)',
 572        'sqlite': 'TEXT',
 573        'geopackage': 'TEXT',
 574        'duckdb': 'TEXT',
 575        'citus': 'JSONB',
 576        'cockroachdb': 'JSONB',
 577        'default': 'TEXT',
 578    },
 579    'numeric': {
 580        'timescaledb': 'NUMERIC',
 581        'timescaledb-ha': 'NUMERIC',
 582        'postgresql': 'NUMERIC',
 583        'postgis': 'NUMERIC',
 584        'mariadb': f'DECIMAL{NUMERIC_PRECISION_FLAVORS["mariadb"]}',
 585        'mysql': f'DECIMAL{NUMERIC_PRECISION_FLAVORS["mysql"]}',
 586        'mssql': f'NUMERIC{NUMERIC_PRECISION_FLAVORS["mssql"]}',
 587        'oracle': 'NUMBER',
 588        'sqlite': 'TEXT',
 589        'geopackage': 'TEXT',
 590        'duckdb': 'TEXT',
 591        'citus': 'NUMERIC',
 592        'cockroachdb': 'NUMERIC',
 593        'default': 'NUMERIC',
 594    },
 595    'uuid': {
 596        'timescaledb': 'UUID',
 597        'timescaledb-ha': 'UUID',
 598        'postgresql': 'UUID',
 599        'postgis': 'UUID',
 600        'mariadb': 'CHAR(36)',
 601        'mysql': 'CHAR(36)',
 602        'mssql': 'UNIQUEIDENTIFIER',
 603        ### I know this is too much space, but erring on the side of caution.
 604        'oracle': 'CHAR(36)',
 605        'sqlite': 'TEXT',
 606        'geopackage': 'TEXT',
 607        'duckdb': 'VARCHAR',
 608        'citus': 'UUID',
 609        'cockroachdb': 'UUID',
 610        'default': 'TEXT',
 611    },
 612    'bytes': {
 613        'timescaledb': 'BYTEA',
 614        'timescaledb-ha': 'BYTEA',
 615        'postgresql': 'BYTEA',
 616        'postgis': 'BYTEA',
 617        'mariadb': 'BLOB',
 618        'mysql': 'BLOB',
 619        'mssql': 'VARBINARY(MAX)',
 620        'oracle': 'BLOB',
 621        'sqlite': 'BLOB',
 622        'geopackage': 'BLOB',
 623        'duckdb': 'BLOB',
 624        'citus': 'BYTEA',
 625        'cockroachdb': 'BYTEA',
 626        'default': 'BLOB',
 627    },
 628    'geometry': {
 629        'timescaledb': 'TEXT',
 630        'timescaledb-ha': 'GEOMETRY',
 631        'postgresql': 'TEXT',
 632        'postgis': 'GEOMETRY',
 633        'mariadb': 'TEXT',
 634        'mysql': 'TEXT',
 635        'mssql': 'NVARCHAR(MAX)',
 636        'oracle': 'NVARCHAR2(2000)',
 637        'sqlite': 'TEXT',
 638        'geopackage': 'GEOMETRY',
 639        'duckdb': 'TEXT',
 640        'citus': 'TEXT',
 641        'cockroachdb': 'TEXT',
 642        'default': 'TEXT',
 643    },
 644    'geography': {
 645        'timescaledb': 'TEXT',
 646        'timescaledb-ha': 'GEOGRAPHY',
 647        'postgresql': 'TEXT',
 648        'postgis': 'GEOGRAPHY',
 649        'mariadb': 'TEXT',
 650        'mysql': 'TEXT',
 651        'mssql': 'NVARCHAR(MAX)',
 652        'oracle': 'NVARCHAR2(2000)',
 653        'sqlite': 'TEXT',
 654        'geopackage': 'TEXT',
 655        'duckdb': 'TEXT',
 656        'citus': 'TEXT',
 657        'cockroachdb': 'TEXT',
 658        'default': 'TEXT',
 659    },
 660}
 661PD_TO_SQLALCHEMY_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {
 662    'int': {
 663        'timescaledb': 'BigInteger',
 664        'timescaledb-ha': 'BigInteger',
 665        'postgresql': 'BigInteger',
 666        'postgis': 'BigInteger',
 667        'mariadb': 'BigInteger',
 668        'mysql': 'BigInteger',
 669        'mssql': 'BigInteger',
 670        'oracle': 'BigInteger',
 671        'sqlite': 'BigInteger',
 672        'geopackage': 'BigInteger',
 673        'duckdb': 'BigInteger',
 674        'citus': 'BigInteger',
 675        'cockroachdb': 'BigInteger',
 676        'default': 'BigInteger',
 677    },
 678    'uint': {
 679        'timescaledb': 'BigInteger',
 680        'timescaledb-ha': 'BigInteger',
 681        'postgresql': 'BigInteger',
 682        'postgis': 'BigInteger',
 683        'mariadb': 'BigInteger',
 684        'mysql': 'BigInteger',
 685        'mssql': 'BigInteger',
 686        'oracle': 'BigInteger',
 687        'sqlite': 'BigInteger',
 688        'geopackage': 'BigInteger',
 689        'duckdb': 'BigInteger',
 690        'citus': 'BigInteger',
 691        'cockroachdb': 'BigInteger',
 692        'default': 'BigInteger',
 693    },
 694    'int8': {
 695        'timescaledb': 'SmallInteger',
 696        'timescaledb-ha': 'SmallInteger',
 697        'postgresql': 'SmallInteger',
 698        'postgis': 'SmallInteger',
 699        'mariadb': 'SmallInteger',
 700        'mysql': 'SmallInteger',
 701        'mssql': 'SmallInteger',
 702        'oracle': 'SmallInteger',
 703        'sqlite': 'SmallInteger',
 704        'geopackage': 'SmallInteger',
 705        'duckdb': 'SmallInteger',
 706        'citus': 'SmallInteger',
 707        'cockroachdb': 'SmallInteger',
 708        'default': 'SmallInteger',
 709    },
 710    'uint8': {
 711        'timescaledb': 'SmallInteger',
 712        'timescaledb-ha': 'SmallInteger',
 713        'postgresql': 'SmallInteger',
 714        'postgis': 'SmallInteger',
 715        'mariadb': 'SmallInteger',
 716        'mysql': 'SmallInteger',
 717        'mssql': 'SmallInteger',
 718        'oracle': 'SmallInteger',
 719        'sqlite': 'SmallInteger',
 720        'geopackage': 'SmallInteger',
 721        'duckdb': 'SmallInteger',
 722        'citus': 'SmallInteger',
 723        'cockroachdb': 'SmallInteger',
 724        'default': 'SmallInteger',
 725    },
 726    'int16': {
 727        'timescaledb': 'SmallInteger',
 728        'timescaledb-ha': 'SmallInteger',
 729        'postgresql': 'SmallInteger',
 730        'postgis': 'SmallInteger',
 731        'mariadb': 'SmallInteger',
 732        'mysql': 'SmallInteger',
 733        'mssql': 'SmallInteger',
 734        'oracle': 'SmallInteger',
 735        'sqlite': 'SmallInteger',
 736        'geopackage': 'SmallInteger',
 737        'duckdb': 'SmallInteger',
 738        'citus': 'SmallInteger',
 739        'cockroachdb': 'SmallInteger',
 740        'default': 'SmallInteger',
 741    },
 742    'int32': {
 743        'timescaledb': 'Integer',
 744        'timescaledb-ha': 'Integer',
 745        'postgresql': 'Integer',
 746        'postgis': 'Integer',
 747        'mariadb': 'Integer',
 748        'mysql': 'Integer',
 749        'mssql': 'Integer',
 750        'oracle': 'Integer',
 751        'sqlite': 'Integer',
 752        'geopackage': 'Integer',
 753        'duckdb': 'Integer',
 754        'citus': 'Integer',
 755        'cockroachdb': 'Integer',
 756        'default': 'Integer',
 757    },
 758    'int64': {
 759        'timescaledb': 'BigInteger',
 760        'timescaledb-ha': 'BigInteger',
 761        'postgresql': 'BigInteger',
 762        'postgis': 'BigInteger',
 763        'mariadb': 'BigInteger',
 764        'mysql': 'BigInteger',
 765        'mssql': 'BigInteger',
 766        'oracle': 'BigInteger',
 767        'sqlite': 'BigInteger',
 768        'geopackage': 'BigInteger',
 769        'duckdb': 'BigInteger',
 770        'citus': 'BigInteger',
 771        'cockroachdb': 'BigInteger',
 772        'default': 'BigInteger',
 773    },
 774    'float': {
 775        'timescaledb': 'Float',
 776        'timescaledb-ha': 'Float',
 777        'postgresql': 'Float',
 778        'postgis': 'Float',
 779        'mariadb': 'Float',
 780        'mysql': 'Float',
 781        'mssql': 'Float',
 782        'oracle': 'Float',
 783        'sqlite': 'Float',
 784        'geopackage': 'Float',
 785        'duckdb': 'Float',
 786        'citus': 'Float',
 787        'cockroachdb': 'Float',
 788        'default': 'Float',
 789    },
 790    'datetime': {
 791        'timescaledb': 'DateTime(timezone=True)',
 792        'timescaledb-ha': 'DateTime(timezone=True)',
 793        'postgresql': 'DateTime(timezone=True)',
 794        'postgis': 'DateTime(timezone=True)',
 795        'mariadb': 'DateTime(timezone=True)',
 796        'mysql': 'DateTime(timezone=True)',
 797        'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET',
 798        'oracle': 'sqlalchemy.dialects.oracle.TIMESTAMP',
 799        'sqlite': 'DateTime(timezone=True)',
 800        'geopackage': 'DateTime(timezone=True)',
 801        'duckdb': 'DateTime(timezone=True)',
 802        'citus': 'DateTime(timezone=True)',
 803        'cockroachdb': 'DateTime(timezone=True)',
 804        'default': 'DateTime(timezone=True)',
 805    },
 806    'datetime64[ns]': {
 807        'timescaledb': 'DateTime',
 808        'timescaledb-ha': 'DateTime',
 809        'postgresql': 'DateTime',
 810        'postgis': 'DateTime',
 811        'mariadb': 'DateTime',
 812        'mysql': 'DateTime',
 813        'mssql': 'sqlalchemy.dialects.mssql.DATETIME2',
 814        'oracle': 'DateTime',
 815        'sqlite': 'DateTime',
 816        'geopackage': 'DateTime',
 817        'duckdb': 'DateTime',
 818        'citus': 'DateTime',
 819        'cockroachdb': 'DateTime',
 820        'default': 'DateTime',
 821    },
 822    'datetime64[ns, UTC]': {
 823        'timescaledb': 'DateTime(timezone=True)',
 824        'timescaledb-ha': 'DateTime(timezone=True)',
 825        'postgresql': 'DateTime(timezone=True)',
 826        'postgis': 'DateTime(timezone=True)',
 827        'mariadb': 'DateTime(timezone=True)',
 828        'mysql': 'DateTime(timezone=True)',
 829        'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET',
 830        'oracle': 'sqlalchemy.dialects.oracle.TIMESTAMP',
 831        'sqlite': 'DateTime(timezone=True)',
 832        'geopackage': 'DateTime(timezone=True)',
 833        'duckdb': 'DateTime(timezone=True)',
 834        'citus': 'DateTime(timezone=True)',
 835        'cockroachdb': 'DateTime(timezone=True)',
 836        'default': 'DateTime(timezone=True)',
 837    },
 838    'datetime64[us]': {
 839        'timescaledb': 'DateTime',
 840        'timescaledb-ha': 'DateTime',
 841        'postgresql': 'DateTime',
 842        'postgis': 'DateTime',
 843        'mariadb': 'DateTime',
 844        'mysql': 'DateTime',
 845        'mssql': 'sqlalchemy.dialects.mssql.DATETIME2',
 846        'oracle': 'DateTime',
 847        'sqlite': 'DateTime',
 848        'geopackage': 'DateTime',
 849        'duckdb': 'DateTime',
 850        'citus': 'DateTime',
 851        'cockroachdb': 'DateTime',
 852        'default': 'DateTime',
 853    },
 854    'datetime64[us, UTC]': {
 855        'timescaledb': 'DateTime(timezone=True)',
 856        'timescaledb-ha': 'DateTime(timezone=True)',
 857        'postgresql': 'DateTime(timezone=True)',
 858        'postgis': 'DateTime(timezone=True)',
 859        'mariadb': 'DateTime(timezone=True)',
 860        'mysql': 'DateTime(timezone=True)',
 861        'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET',
 862        'oracle': 'sqlalchemy.dialects.oracle.TIMESTAMP',
 863        'sqlite': 'DateTime(timezone=True)',
 864        'geopackage': 'DateTime(timezone=True)',
 865        'duckdb': 'DateTime(timezone=True)',
 866        'citus': 'DateTime(timezone=True)',
 867        'cockroachdb': 'DateTime(timezone=True)',
 868        'default': 'DateTime(timezone=True)',
 869    },
 870    'datetime64[ms]': {
 871        'timescaledb': 'DateTime',
 872        'timescaledb-ha': 'DateTime',
 873        'postgresql': 'DateTime',
 874        'postgis': 'DateTime',
 875        'mariadb': 'DateTime',
 876        'mysql': 'DateTime',
 877        'mssql': 'sqlalchemy.dialects.mssql.DATETIME2',
 878        'oracle': 'DateTime',
 879        'sqlite': 'DateTime',
 880        'geopackage': 'DateTime',
 881        'duckdb': 'DateTime',
 882        'citus': 'DateTime',
 883        'cockroachdb': 'DateTime',
 884        'default': 'DateTime',
 885    },
 886    'datetime64[ms, UTC]': {
 887        'timescaledb': 'DateTime(timezone=True)',
 888        'timescaledb-ha': 'DateTime(timezone=True)',
 889        'postgresql': 'DateTime(timezone=True)',
 890        'postgis': 'DateTime(timezone=True)',
 891        'mariadb': 'DateTime(timezone=True)',
 892        'mysql': 'DateTime(timezone=True)',
 893        'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET',
 894        'oracle': 'sqlalchemy.dialects.oracle.TIMESTAMP',
 895        'sqlite': 'DateTime(timezone=True)',
 896        'geopackage': 'DateTime(timezone=True)',
 897        'duckdb': 'DateTime(timezone=True)',
 898        'citus': 'DateTime(timezone=True)',
 899        'cockroachdb': 'DateTime(timezone=True)',
 900        'default': 'DateTime(timezone=True)',
 901    },
 902    'datetime64[s]': {
 903        'timescaledb': 'DateTime',
 904        'timescaledb-ha': 'DateTime',
 905        'postgresql': 'DateTime',
 906        'postgis': 'DateTime',
 907        'mariadb': 'DateTime',
 908        'mysql': 'DateTime',
 909        'mssql': 'sqlalchemy.dialects.mssql.DATETIME2',
 910        'oracle': 'DateTime',
 911        'sqlite': 'DateTime',
 912        'geopackage': 'DateTime',
 913        'duckdb': 'DateTime',
 914        'citus': 'DateTime',
 915        'cockroachdb': 'DateTime',
 916        'default': 'DateTime',
 917    },
 918    'datetime64[s, UTC]': {
 919        'timescaledb': 'DateTime(timezone=True)',
 920        'timescaledb-ha': 'DateTime(timezone=True)',
 921        'postgresql': 'DateTime(timezone=True)',
 922        'postgis': 'DateTime(timezone=True)',
 923        'mariadb': 'DateTime(timezone=True)',
 924        'mysql': 'DateTime(timezone=True)',
 925        'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET',
 926        'oracle': 'sqlalchemy.dialects.oracle.TIMESTAMP',
 927        'sqlite': 'DateTime(timezone=True)',
 928        'geopackage': 'DateTime(timezone=True)',
 929        'duckdb': 'DateTime(timezone=True)',
 930        'citus': 'DateTime(timezone=True)',
 931        'cockroachdb': 'DateTime(timezone=True)',
 932        'default': 'DateTime(timezone=True)',
 933    },
 934    'date': {
 935        'timescaledb': 'Date',
 936        'timescaledb-ha': 'Date',
 937        'postgresql': 'Date',
 938        'postgis': 'Date',
 939        'mariadb': 'Date',
 940        'mysql': 'Date',
 941        'mssql': 'Date',
 942        'oracle': 'Date',
 943        'sqlite': 'Date',
 944        'geopackage': 'Date',
 945        'duckdb': 'Date',
 946        'citus': 'Date',
 947        'cockroachdb': 'Date',
 948        'default': 'Date',
 949    },
 950    'bool': {
 951        'timescaledb': 'Boolean',
 952        'timescaledb-ha': 'Boolean',
 953        'postgresql': 'Boolean',
 954        'postgis': 'Boolean',
 955        'mariadb': 'Integer',
 956        'mysql': 'Integer',
 957        'mssql': 'sqlalchemy.dialects.mssql.BIT',
 958        'oracle': 'Integer',
 959        'sqlite': 'Float',
 960        'geopackage': 'Float',
 961        'duckdb': 'Boolean',
 962        'citus': 'Boolean',
 963        'cockroachdb': 'Boolean',
 964        'default': 'Boolean',
 965    },
 966    'bool[pyarrow]': {
 967        'timescaledb': 'Boolean',
 968        'timescaledb-ha': 'Boolean',
 969        'postgresql': 'Boolean',
 970        'postgis': 'Boolean',
 971        'mariadb': 'Integer',
 972        'mysql': 'Integer',
 973        'mssql': 'sqlalchemy.dialects.mssql.BIT',
 974        'oracle': 'Integer',
 975        'sqlite': 'Float',
 976        'geopackage': 'Float',
 977        'duckdb': 'Boolean',
 978        'citus': 'Boolean',
 979        'cockroachdb': 'Boolean',
 980        'default': 'Boolean',
 981    },
 982    'boolean': {
 983        'timescaledb': 'Boolean',
 984        'timescaledb-ha': 'Boolean',
 985        'postgresql': 'Boolean',
 986        'postgis': 'Boolean',
 987        'mariadb': 'Integer',
 988        'mysql': 'Integer',
 989        'mssql': 'sqlalchemy.dialects.mssql.BIT',
 990        'oracle': 'Integer',
 991        'sqlite': 'Float',
 992        'geopackage': 'Float',
 993        'duckdb': 'Boolean',
 994        'citus': 'Boolean',
 995        'cockroachdb': 'Boolean',
 996        'default': 'Boolean',
 997    },
 998    'object': {
 999        'timescaledb': 'UnicodeText',
1000        'timescaledb-ha': 'UnicodeText',
1001        'postgresql': 'UnicodeText',
1002        'postgis': 'UnicodeText',
1003        'mariadb': 'UnicodeText',
1004        'mysql': 'UnicodeText',
1005        'mssql': 'UnicodeText',
1006        'oracle': 'UnicodeText',
1007        'sqlite': 'UnicodeText',
1008        'geopackage': 'UnicodeText',
1009        'duckdb': 'UnicodeText',
1010        'citus': 'UnicodeText',
1011        'cockroachdb': 'UnicodeText',
1012        'default': 'UnicodeText',
1013    },
1014    'string': {
1015        'timescaledb': 'UnicodeText',
1016        'timescaledb-ha': 'UnicodeText',
1017        'postgresql': 'UnicodeText',
1018        'postgis': 'UnicodeText',
1019        'mariadb': 'UnicodeText',
1020        'mysql': 'UnicodeText',
1021        'mssql': 'UnicodeText',
1022        'oracle': 'UnicodeText',
1023        'sqlite': 'UnicodeText',
1024        'geopackage': 'UnicodeText',
1025        'duckdb': 'UnicodeText',
1026        'citus': 'UnicodeText',
1027        'cockroachdb': 'UnicodeText',
1028        'default': 'UnicodeText',
1029    },
1030    'json': {
1031        'timescaledb': 'sqlalchemy.dialects.postgresql.JSONB',
1032        'timescaledb-ha': 'sqlalchemy.dialects.postgresql.JSONB',
1033        'postgresql': 'sqlalchemy.dialects.postgresql.JSONB',
1034        'postgis': 'sqlalchemy.dialects.postgresql.JSONB',
1035        'mariadb': 'UnicodeText',
1036        'mysql': 'UnicodeText',
1037        'mssql': 'UnicodeText',
1038        'oracle': 'UnicodeText',
1039        'sqlite': 'UnicodeText',
1040        'geopackage': 'UnicodeText',
1041        'duckdb': 'TEXT',
1042        'citus': 'sqlalchemy.dialects.postgresql.JSONB',
1043        'cockroachdb': 'sqlalchemy.dialects.postgresql.JSONB',
1044        'default': 'UnicodeText',
1045    },
1046    'numeric': {
1047        'timescaledb': 'Numeric',
1048        'timescaledb-ha': 'Numeric',
1049        'postgresql': 'Numeric',
1050        'postgis': 'Numeric',
1051        'mariadb': 'Numeric',
1052        'mysql': 'Numeric',
1053        'mssql': 'Numeric',
1054        'oracle': 'Numeric',
1055        'sqlite': 'UnicodeText',
1056        'geopackage': 'UnicodeText',
1057        'duckdb': 'Numeric',
1058        'citus': 'Numeric',
1059        'cockroachdb': 'Numeric',
1060        'default': 'Numeric',
1061    },
1062    'uuid': {
1063        'timescaledb': 'Uuid',
1064        'timescaledb-ha': 'Uuid',
1065        'postgresql': 'Uuid',
1066        'postgis': 'Uuid',
1067        'mariadb': 'sqlalchemy.dialects.mysql.CHAR(36)',
1068        'mysql': 'sqlalchemy.dialects.mysql.CHAR(36)',
1069        'mssql': 'Uuid',
1070        'oracle': 'sqlalchemy.dialects.oracle.CHAR(36)',
1071        'sqlite': 'UnicodeText',
1072        'geopackage': 'UnicodeText',
1073        'duckdb': 'UnicodeText',
1074        'citus': 'Uuid',
1075        'cockroachdb': 'Uuid',
1076        'default': 'Uuid',
1077    },
1078    'binary[pyarrow]': {
1079        'timescaledb': 'LargeBinary',
1080        'timescaledb-ha': 'LargeBinary',
1081        'postgresql': 'LargeBinary',
1082        'postgis': 'LargeBinary',
1083        'mariadb': 'LargeBinary',
1084        'mysql': 'LargeBinary',
1085        'mssql': 'LargeBinary',
1086        'oracle': 'LargeBinary',
1087        'sqlite': 'LargeBinary',
1088        'geopackage': 'LargeBinary',
1089        'duckdb': 'LargeBinary',
1090        'citus': 'LargeBinary',
1091        'cockroachdb': 'LargeBinary',
1092        'default': 'LargeBinary',
1093    },
1094    'bytes': {
1095        'timescaledb': 'LargeBinary',
1096        'timescaledb-ha': 'LargeBinary',
1097        'postgresql': 'LargeBinary',
1098        'postgis': 'LargeBinary',
1099        'mariadb': 'LargeBinary',
1100        'mysql': 'LargeBinary',
1101        'mssql': 'LargeBinary',
1102        'oracle': 'LargeBinary',
1103        'sqlite': 'LargeBinary',
1104        'geopackage': 'LargeBinary',
1105        'duckdb': 'LargeBinary',
1106        'citus': 'LargeBinary',
1107        'cockroachdb': 'LargeBinary',
1108        'default': 'LargeBinary',
1109    },
1110    'geometry': {
1111        'timescaledb': 'UnicodeText',
1112        'timescaledb-ha': 'geoalchemy2.Geometry',
1113        'postgresql': 'UnicodeText',
1114        'postgis': 'geoalchemy2.Geometry',
1115        'mariadb': 'UnicodeText',
1116        'mysql': 'UnicodeText',
1117        'mssql': 'UnicodeText',
1118        'oracle': 'UnicodeText',
1119        'sqlite': 'UnicodeText',
1120        'geopackage': 'LargeBinary',
1121        'duckdb': 'UnicodeText',
1122        'citus': 'UnicodeText',
1123        'cockroachdb': 'UnicodeText',
1124        'default': 'UnicodeText',
1125    },
1126    'geography': {
1127        'timescaledb': 'UnicodeText',
1128        'timescaledb-ha': 'geoalchemy2.Geography',
1129        'postgresql': 'UnicodeText',
1130        'postgis': 'geoalchemy2.Geography',
1131        'mariadb': 'UnicodeText',
1132        'mysql': 'UnicodeText',
1133        'mssql': 'UnicodeText',
1134        'oracle': 'UnicodeText',
1135        'sqlite': 'UnicodeText',
1136        'geopackage': 'UnicodeText',
1137        'duckdb': 'UnicodeText',
1138        'citus': 'UnicodeText',
1139        'cockroachdb': 'UnicodeText',
1140        'default': 'UnicodeText',
1141    },
1142}
1143
1144AUTO_INCREMENT_COLUMN_FLAVORS: Dict[str, str] = {
1145    'timescaledb': 'GENERATED BY DEFAULT AS IDENTITY',
1146    'timescaledb-ha': 'GENERATED BY DEFAULT AS IDENTITY',
1147    'postgresql': 'GENERATED BY DEFAULT AS IDENTITY',
1148    'postgis': 'GENERATED BY DEFAULT AS IDENTITY',
1149    'mariadb': 'AUTO_INCREMENT',
1150    'mysql': 'AUTO_INCREMENT',
1151    'mssql': 'IDENTITY(1,1)',
1152    'oracle': 'GENERATED BY DEFAULT ON NULL AS IDENTITY',
1153    'sqlite': 'AUTOINCREMENT',
1154    'geopackage': 'AUTOINCREMENT',
1155    'duckdb': 'GENERATED BY DEFAULT',
1156    'citus': 'GENERATED BY DEFAULT',
1157    'cockroachdb': 'GENERATED BY DEFAULT AS IDENTITY',
1158    'default': 'GENERATED BY DEFAULT AS IDENTITY',
1159}
1160
1161
1162def get_pd_type_from_db_type(db_type: str, allow_custom_dtypes: bool = True) -> str:
1163    """
1164    Parse a database type to a pandas data type.
1165
1166    Parameters
1167    ----------
1168    db_type: str
1169        The database type, e.g. `DATETIME`, `BIGINT`, etc.
1170
1171    allow_custom_dtypes: bool, default False
1172        If `True`, allow for custom data types like `json` and `str`.
1173
1174    Returns
1175    -------
1176    The equivalent datatype for a pandas DataFrame.
1177    """
1178    from meerschaum.utils.dtypes import are_dtypes_equal, get_geometry_type_srid
1179    def parse_custom(_pd_type: str, _db_type: str) -> str:
1180        if 'json' in _db_type.lower():
1181            return 'json'
1182        if are_dtypes_equal(_pd_type, 'numeric') and _pd_type != 'object':
1183            precision, scale = get_numeric_precision_scale(None, dtype=_db_type.upper())
1184            if precision and scale:
1185                return f"numeric[{precision},{scale}]"
1186        if are_dtypes_equal(_pd_type, 'geometry') and _pd_type != 'object':
1187            geometry_type, srid = get_geometry_type_srid(_db_type.upper())
1188            modifiers = [str(modifier) for modifier in (geometry_type, srid) if modifier]
1189            typ = "geometry" if 'geography' not in _pd_type.lower() else 'geography'
1190            if not modifiers:
1191                return typ
1192            return f"{typ}[{', '.join(modifiers)}]"
1193        return _pd_type
1194
1195    pd_type = DB_TO_PD_DTYPES.get(db_type.upper().split('(', maxsplit=1)[0].strip(), None)
1196    if pd_type is not None:
1197        return (
1198            parse_custom(pd_type, db_type)
1199            if allow_custom_dtypes
1200            else pd_type
1201        )
1202    for db_t, pd_t in DB_TO_PD_DTYPES['substrings'].items():
1203        if db_t in db_type.upper():
1204            return (
1205                parse_custom(pd_t, db_t)
1206                if allow_custom_dtypes
1207                else pd_t
1208            )
1209    return DB_TO_PD_DTYPES['default']
1210
1211
1212def get_db_type_from_pd_type(
1213    pd_type: str,
1214    flavor: str = 'default',
1215    as_sqlalchemy: bool = False,
1216) -> Union[str, 'sqlalchemy.sql.visitors.TraversibleType']:
1217    """
1218    Parse a Pandas data type into a flavor's database type.
1219
1220    Parameters
1221    ----------
1222    pd_type: str
1223        The Pandas datatype. This must be a string, not the actual dtype object.
1224
1225    flavor: str, default 'default'
1226        The flavor of the database to be mapped to.
1227
1228    as_sqlalchemy: bool, default False
1229        If `True`, return a type from `sqlalchemy.types`.
1230
1231    Returns
1232    -------
1233    The database data type for the incoming Pandas data type.
1234    If nothing can be found, a warning will be thrown and 'TEXT' will be returned.
1235    """
1236    from meerschaum.utils.warnings import warn
1237    from meerschaum.utils.packages import attempt_import
1238    from meerschaum.utils.dtypes import are_dtypes_equal, MRSM_ALIAS_DTYPES, get_geometry_type_srid
1239    from meerschaum.utils.misc import parse_arguments_str
1240    sqlalchemy_types = attempt_import('sqlalchemy.types', lazy=False)
1241
1242    types_registry = (
1243        PD_TO_DB_DTYPES_FLAVORS
1244        if not as_sqlalchemy
1245        else PD_TO_SQLALCHEMY_DTYPES_FLAVORS
1246    )
1247
1248    precision, scale = None, None
1249    geometry_type, geometry_srid = None, None
1250    og_pd_type = pd_type
1251    if pd_type in MRSM_ALIAS_DTYPES:
1252        pd_type = MRSM_ALIAS_DTYPES[pd_type]
1253
1254    ### Check whether we are able to match this type (e.g. pyarrow support).
1255    found_db_type = False
1256    if (
1257        pd_type not in types_registry
1258        and not any(
1259            pd_type.startswith(f'{typ}[')
1260            for typ in ('numeric', 'geometry', 'geography')
1261        )
1262    ):
1263        for mapped_pd_type in types_registry:
1264            if are_dtypes_equal(mapped_pd_type, pd_type):
1265                pd_type = mapped_pd_type
1266                found_db_type = True
1267                break
1268    elif (pd_type.startswith('geometry[') or pd_type.startswith('geography[')):
1269        og_pd_type = pd_type
1270        pd_type = 'geometry' if 'geometry' in pd_type else 'geography'
1271        geometry_type, geometry_srid = get_geometry_type_srid(og_pd_type)
1272        found_db_type = True
1273    elif pd_type.startswith('numeric['):
1274        og_pd_type = pd_type
1275        pd_type = 'numeric'
1276        precision, scale = get_numeric_precision_scale(flavor, og_pd_type)
1277        found_db_type = True
1278    else:
1279        found_db_type = True
1280
1281    if not found_db_type:
1282        warn(f"Unknown Pandas data type '{pd_type}'. Falling back to 'TEXT'.", stacklevel=3)
1283        return (
1284            'TEXT'
1285            if not as_sqlalchemy
1286            else sqlalchemy_types.UnicodeText
1287        )
1288    flavor_types = types_registry.get(
1289        pd_type,
1290        {
1291            'default': (
1292                'TEXT'
1293                if not as_sqlalchemy
1294                else 'UnicodeText'
1295            ),
1296        },
1297    )
1298    default_flavor_type = flavor_types.get(
1299        'default',
1300        (
1301            'TEXT'
1302            if not as_sqlalchemy
1303            else 'UnicodeText'
1304        ),
1305    )
1306    if flavor not in flavor_types:
1307        warn(f"Unknown flavor '{flavor}'. Falling back to '{default_flavor_type}' (default).")
1308    db_type = flavor_types.get(flavor, default_flavor_type)
1309    if not as_sqlalchemy:
1310        if precision is not None and scale is not None:
1311            db_type_bare = db_type.split('(', maxsplit=1)[0]
1312            return f"{db_type_bare}({precision},{scale})"
1313        if geometry_type is not None and geometry_srid is not None:
1314            if 'geometry' not in db_type.lower() and 'geography' not in db_type.lower():
1315                return db_type
1316            db_type_bare = db_type.split('(', maxsplit=1)[0]
1317            return f"{db_type_bare}({geometry_type.upper()}, {geometry_srid})"
1318        return db_type
1319
1320    if db_type.startswith('sqlalchemy.dialects'):
1321        dialect, typ_class_name = db_type.replace('sqlalchemy.dialects.', '').split('.', maxsplit=2)
1322        cls_args, cls_kwargs = None, None
1323        if '(' in typ_class_name:
1324            typ_class_name, args_str = typ_class_name.split('(', maxsplit=1)
1325            args_str = args_str.rstrip(')')
1326            cls_args, cls_kwargs = parse_arguments_str(args_str)
1327        sqlalchemy_dialects_flavor_module = attempt_import(f'sqlalchemy.dialects.{dialect}')
1328        cls = getattr(sqlalchemy_dialects_flavor_module, typ_class_name)
1329        if cls_args is None:
1330            return cls
1331        return cls(*cls_args, **cls_kwargs)
1332
1333    if 'geometry' in db_type.lower() or 'geography' in db_type.lower():
1334        geoalchemy2 = attempt_import('geoalchemy2', lazy=False)
1335        geometry_class = (
1336            geoalchemy2.Geometry
1337            if 'geometry' in db_type.lower()
1338            else geoalchemy2.Geography
1339        )
1340        if geometry_type is None or geometry_srid is None:
1341            return geometry_class
1342        return geometry_class(geometry_type=geometry_type, srid=geometry_srid)
1343
1344    if 'numeric' in db_type.lower():
1345        if precision is None or scale is None:
1346            return sqlalchemy_types.Numeric
1347        return sqlalchemy_types.Numeric(precision, scale)
1348
1349    cls_args, cls_kwargs = None, None
1350    typ_class_name = db_type
1351    if '(' in db_type:
1352        typ_class_name, args_str = db_type.split('(', maxsplit=1)
1353        args_str = args_str.rstrip(')')
1354        cls_args, cls_kwargs = parse_arguments_str(args_str)
1355
1356    cls = getattr(sqlalchemy_types, typ_class_name)
1357    if cls_args is None:
1358        return cls
1359    return cls(*cls_args, **cls_kwargs)
1360
1361
1362def get_numeric_precision_scale(
1363    flavor: str,
1364    dtype: Optional[str] = None,
1365) -> Union[Tuple[int, int], Tuple[None, None]]:
1366    """
1367    Return the precision and scale to use for a numeric column for a given database flavor.
1368
1369    Parameters
1370    ----------
1371    flavor: str
1372        The database flavor for which to return the precision and scale.
1373    
1374    dtype: Optional[str], default None
1375        If provided, return the precision and scale provided in the dtype (if applicable).
1376        If all caps, treat this as a DB type.
1377
1378    Returns
1379    -------
1380    A tuple of ints or a tuple of Nones.
1381    """
1382    if not dtype:
1383        return None, None
1384
1385    lbracket = '[' if '[' in dtype else '('
1386    rbracket = ']' if lbracket == '[' else ')'
1387    if lbracket in dtype and dtype.count(',') == 1 and dtype.endswith(rbracket):
1388        try:
1389            parts = dtype.split(lbracket, maxsplit=1)[-1].rstrip(rbracket).split(',', maxsplit=1)
1390            return int(parts[0].strip()), int(parts[1].strip())
1391        except Exception:
1392            pass
1393
1394    return NUMERIC_PRECISION_FLAVORS.get(flavor, (None, None))
NUMERIC_PRECISION_FLAVORS: Dict[str, Tuple[int, int]] = {'mariadb': (38, 20), 'mysql': (38, 20), 'mssql': (28, 10)}
NUMERIC_AS_TEXT_FLAVORS = {'geopackage', 'sqlite', 'duckdb'}
TIMEZONE_NAIVE_FLAVORS = {'mariadb', 'mysql', 'oracle'}
DB_FLAVORS_CAST_DTYPES = {'mariadb': {'BIGINT': 'DECIMAL', 'TINYINT': 'SIGNED INT', 'TEXT': 'CHAR(10000) CHARACTER SET utf8', 'BOOL': 'SIGNED INT', 'BOOLEAN': 'SIGNED INT', 'DOUBLE PRECISION': 'DECIMAL', 'DOUBLE': 'DECIMAL', 'FLOAT': 'DECIMAL', 'NUMERIC': 'NUMERIC(38, 20)', 'DECIMAL': 'DECIMAL(38, 20)'}, 'mysql': {'BIGINT': 'DECIMAL', 'TINYINT': 'SIGNED INT', 'TEXT': 'CHAR(10000) CHARACTER SET utf8', 'INT': 'SIGNED INT', 'INTEGER': 'SIGNED INT', 'BOOL': 'SIGNED INT', 'BOOLEAN': 'SIGNED INT', 'DOUBLE PRECISION': 'DECIMAL', 'DOUBLE': 'DECIMAL', 'FLOAT': 'DECIMAL', 'NUMERIC': 'NUMERIC(38, 20)', 'DECIMAL': 'DECIMAL(38, 20)'}, 'sqlite': {'BOOLEAN': 'INTEGER', 'REAL': 'FLOAT'}, 'geopackage': {'BOOLEAN': 'INTEGER', 'REAL': 'FLOAT'}, 'oracle': {'NVARCHAR(2000)': 'NVARCHAR2(2000)', 'NVARCHAR': 'NVARCHAR2(2000)', 'NVARCHAR2': 'NVARCHAR2(2000)', 'CHAR': 'CHAR(36)'}, 'mssql': {'NVARCHAR COLLATE "SQL Latin1 General CP1 CI AS"': 'NVARCHAR(MAX)', 'NVARCHAR COLLATE "SQL_Latin1_General_CP1_CI_AS"': 'NVARCHAR(MAX)', 'VARCHAR COLLATE "SQL Latin1 General CP1 CI AS"': 'NVARCHAR(MAX)', 'VARCHAR COLLATE "SQL_Latin1_General_CP1_CI_AS"': 'NVARCHAR(MAX)', 'NVARCHAR': 'NVARCHAR(MAX)', 'NUMERIC': 'NUMERIC(28, 10)', 'DECIMAL': 'DECIMAL(28, 10)'}}
DB_TO_PD_DTYPES: Dict[str, Union[str, Dict[str, str]]] = {'FLOAT': 'float64[pyarrow]', 'REAL': 'float64[pyarrow]', 'DOUBLE_PRECISION': 'float64[pyarrow]', 'DOUBLE': 'float64[pyarrow]', 'DECIMAL': 'numeric', 'BIGINT': 'int64[pyarrow]', 'INT': 'int32[pyarrow]', 'INTEGER': 'int32[pyarrow]', 'NUMBER': 'numeric', 'NUMERIC': 'numeric', 'GEOMETRY': 'geometry', 'GEOMETRY(GEOMETRY)': 'geometry', 'TIMESTAMP': 'datetime64[us]', 'TIMESTAMP WITHOUT TIMEZONE': 'datetime64[us]', 'TIMESTAMP WITH TIMEZONE': 'datetime64[us, UTC]', 'TIMESTAMP WITH TIME ZONE': 'datetime64[us, UTC]', 'TIMESTAMPTZ': 'datetime64[us, UTC]', 'DATE': 'date', 'DATETIME': 'datetime64[us]', 'DATETIME2': 'datetime64[us]', 'DATETIME2(6)': 'datetime64[us]', 'DATETIME2(3)': 'datetime64[ms]', 'DATETIME2(0)': 'datetime64[s]', 'DATETIMEOFFSET': 'datetime64[us, UTC]', 'DATETIMEOFFSET(6)': 'datetime64[us, UTC]', 'DATETIMEOFFSET(3)': 'datetime64[ms, UTC]', 'DATETIMEOFFSET(0)': 'datetime64[s, UTC]', 'TEXT': 'string[pyarrow]', 'VARCHAR': 'string[pyarrow]', 'CLOB': 'string[pyarrow]', 'BOOL': 'bool[pyarrow]', 'BOOLEAN': 'bool[pyarrow]', 'BOOLEAN()': 'bool[pyarrow]', 'TINYINT': 'bool[pyarrow]', 'TINYINT(1)': 'bool[pyarrow]', 'BIT': 'bool[pyarrow]', 'BIT(1)': 'bool[pyarrow]', 'JSON': 'json', 'JSONB': 'json', 'UUID': 'uuid', 'UNIQUEIDENTIFIER': 'uuid', 'BYTEA': 'bytes', 'BLOB': 'bytes', 'VARBINARY': 'bytes', 'VARBINARY(MAX)': 'bytes', 'substrings': {'CHAR': 'string[pyarrow]', 'TIMESTAMP': 'datetime64[us]', 'TIME': 'datetime64[us]', 'DATE': 'date', 'DOUBLE': 'double[pyarrow]', 'DECIMAL': 'numeric', 'NUMERIC': 'numeric', 'NUMBER': 'numeric', 'INT': 'int64[pyarrow]', 'BOOL': 'bool[pyarrow]', 'JSON': 'json', 'BYTE': 'bytes', 'LOB': 'bytes', 'BINARY': 'bytes', 'GEOMETRY': 'geometry', 'GEOGRAPHY': 'geography'}, 'default': 'object'}
PD_TO_DB_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {'int': {'timescaledb': 'BIGINT', 'timescaledb-ha': 'BIGINT', 'postgresql': 'BIGINT', 'postgis': 'BIGINT', 'mariadb': 'BIGINT', 'mysql': 'BIGINT', 'mssql': 'BIGINT', 'oracle': 'INT', 'sqlite': 'BIGINT', 'geopackage': 'BIGINT', 'duckdb': 'BIGINT', 'citus': 'BIGINT', 'cockroachdb': 'BIGINT', 'default': 'INT'}, 'uint': {'timescaledb': 'BIGINT', 'timescaledb-ha': 'BIGINT', 'postgresql': 'BIGINT', 'postgis': 'BIGINT', 'mariadb': 'BIGINT', 'mysql': 'BIGINT', 'mssql': 'BIGINT', 'oracle': 'INT', 'sqlite': 'BIGINT', 'geopackage': 'BIGINT', 'duckdb': 'BIGINT', 'citus': 'BIGINT', 'cockroachdb': 'BIGINT', 'default': 'INT'}, 'int8': {'timescaledb': 'SMALLINT', 'timescaledb-ha': 'SMALLINT', 'postgresql': 'SMALLINT', 'postgis': 'SMALLINT', 'mariadb': 'SMALLINT', 'mysql': 'SMALLINT', 'mssql': 'SMALLINT', 'oracle': 'INT', 'sqlite': 'INT', 'geopackage': 'INT', 'duckdb': 'SMALLINT', 'citus': 'SMALLINT', 'cockroachdb': 'SMALLINT', 'default': 'INT'}, 'uint8': {'timescaledb': 'SMALLINT', 'timescaledb-ha': 'SMALLINT', 'postgresql': 'SMALLINT', 'postgis': 'SMALLINT', 'mariadb': 'SMALLINT', 'mysql': 'SMALLINT', 'mssql': 'SMALLINT', 'oracle': 'INT', 'sqlite': 'INT', 'geopackage': 'INT', 'duckdb': 'SMALLINT', 'citus': 'SMALLINT', 'cockroachdb': 'SMALLINT', 'default': 'INT'}, 'int16': {'timescaledb': 'SMALLINT', 'timescaledb-ha': 'SMALLINT', 'postgresql': 'SMALLINT', 'postgis': 'SMALLINT', 'mariadb': 'SMALLINT', 'mysql': 'SMALLINT', 'mssql': 'SMALLINT', 'oracle': 'INT', 'sqlite': 'INT', 'geopackage': 'INT', 'duckdb': 'SMALLINT', 'citus': 'SMALLINT', 'cockroachdb': 'SMALLINT', 'default': 'INT'}, 'int32': {'timescaledb': 'INT', 'timescaledb-ha': 'INT', 'postgresql': 'INT', 'postgis': 'INT', 'mariadb': 'INT', 'mysql': 'INT', 'mssql': 'INT', 'oracle': 'INT', 'sqlite': 'INT', 'geopackage': 'INT', 'duckdb': 'INT', 'citus': 'INT', 'cockroachdb': 'INT', 'default': 'INT'}, 'int64': {'timescaledb': 'BIGINT', 'timescaledb-ha': 'BIGINT', 'postgresql': 'BIGINT', 'postgis': 'BIGINT', 'mariadb': 'BIGINT', 'mysql': 'BIGINT', 'mssql': 'BIGINT', 'oracle': 'INT', 'sqlite': 'BIGINT', 'geopackage': 'BIGINT', 'duckdb': 'BIGINT', 'citus': 'BIGINT', 'cockroachdb': 'BIGINT', 'default': 'INT'}, 'float': {'timescaledb': 'DOUBLE PRECISION', 'timescaledb-ha': 'DOUBLE PRECISION', 'postgresql': 'DOUBLE PRECISION', 'postgis': 'DOUBLE PRECISION', 'mariadb': 'DOUBLE PRECISION', 'mysql': 'DOUBLE PRECISION', 'mssql': 'FLOAT', 'oracle': 'FLOAT', 'sqlite': 'FLOAT', 'geopackage': 'FLOAT', 'duckdb': 'DOUBLE PRECISION', 'citus': 'DOUBLE PRECISION', 'cockroachdb': 'DOUBLE PRECISION', 'default': 'DOUBLE'}, 'double': {'timescaledb': 'DOUBLE PRECISION', 'timescaledb-ha': 'DOUBLE PRECISION', 'postgresql': 'DOUBLE PRECISION', 'postgis': 'DOUBLE PRECISION', 'mariadb': 'DOUBLE PRECISION', 'mysql': 'DOUBLE PRECISION', 'mssql': 'FLOAT', 'oracle': 'FLOAT', 'sqlite': 'FLOAT', 'geopackage': 'FLOAT', 'duckdb': 'DOUBLE PRECISION', 'citus': 'DOUBLE PRECISION', 'cockroachdb': 'DOUBLE PRECISION', 'default': 'DOUBLE'}, 'datetime64[ns]': {'timescaledb': 'TIMESTAMP(6)', 'timescaledb-ha': 'TIMESTAMP(6)', 'postgresql': 'TIMESTAMP(6)', 'postgis': 'TIMESTAMP(6)', 'mariadb': 'DATETIME', 'mysql': 'DATETIME', 'mssql': 'DATETIME2(7)', 'oracle': 'TIMESTAMP(9)', 'sqlite': 'DATETIME', 'geopackage': 'DATETIME', 'duckdb': 'TIMESTAMP(6)', 'citus': 'TIMESTAMP(6)', 'cockroachdb': 'TIMESTAMP(6)', 'default': 'DATETIME'}, 'datetime64[ns, UTC]': {'timescaledb': 'TIMESTAMPTZ(6)', 'timescaledb-ha': 'TIMESTAMPTZ(6)', 'postgresql': 'TIMESTAMPTZ(9)', 'postgis': 'TIMESTAMPTZ(6)', 'mariadb': 'DATETIME', 'mysql': 'DATETIME', 'mssql': 'DATETIMEOFFSET(7)', 'oracle': 'TIMESTAMP(9)', 'sqlite': 'TIMESTAMP', 'geopackage': 'TIMESTAMP', 'duckdb': 'TIMESTAMPTZ', 'citus': 'TIMESTAMPTZ(6)', 'cockroachdb': 'TIMESTAMPTZ(6)', 'default': 'TIMESTAMPTZ'}, 'datetime64[us]': {'timescaledb': 'TIMESTAMP(6)', 'timescaledb-ha': 'TIMESTAMP(6)', 'postgresql': 'TIMESTAMP(6)', 'postgis': 'TIMESTAMP(6)', 'mariadb': 'DATETIME', 'mysql': 'DATETIME', 'mssql': 'DATETIME2(6)', 'oracle': 'TIMESTAMP(6)', 'sqlite': 'DATETIME', 'geopackage': 'DATETIME', 'duckdb': 'TIMESTAMP(6)', 'citus': 'TIMESTAMP(6)', 'cockroachdb': 'TIMESTAMP', 'default': 'DATETIME'}, 'datetime64[us, UTC]': {'timescaledb': 'TIMESTAMPTZ(6)', 'timescaledb-ha': 'TIMESTAMPTZ(6)', 'postgresql': 'TIMESTAMPTZ(6)', 'postgis': 'TIMESTAMPTZ(6)', 'mariadb': 'DATETIME', 'mysql': 'DATETIME', 'mssql': 'DATETIMEOFFSET(6)', 'oracle': 'TIMESTAMP(6)', 'sqlite': 'TIMESTAMP', 'geopackage': 'TIMESTAMP', 'duckdb': 'TIMESTAMPTZ', 'citus': 'TIMESTAMPTZ(6)', 'cockroachdb': 'TIMESTAMPTZ(6)', 'default': 'TIMESTAMPTZ'}, 'datetime64[ms]': {'timescaledb': 'TIMESTAMP(3)', 'timescaledb-ha': 'TIMESTAMP(3)', 'postgresql': 'TIMESTAMP(3)', 'postgis': 'TIMESTAMP(3)', 'mariadb': 'DATETIME', 'mysql': 'DATETIME', 'mssql': 'DATETIME2(3)', 'oracle': 'TIMESTAMP(3)', 'sqlite': 'DATETIME', 'geopackage': 'DATETIME', 'duckdb': 'TIMESTAMP(3)', 'citus': 'TIMESTAMP(3)', 'cockroachdb': 'TIMESTAMP(3)', 'default': 'DATETIME'}, 'datetime64[ms, UTC]': {'timescaledb': 'TIMESTAMPTZ(3)', 'timescaledb-ha': 'TIMESTAMPTZ(3)', 'postgresql': 'TIMESTAMPTZ(3)', 'postgis': 'TIMESTAMPTZ(3)', 'mariadb': 'DATETIME', 'mysql': 'DATETIME', 'mssql': 'DATETIMEOFFSET(3)', 'oracle': 'TIMESTAMP(3)', 'sqlite': 'TIMESTAMP', 'geopackage': 'TIMESTAMP', 'duckdb': 'TIMESTAMPTZ', 'citus': 'TIMESTAMPTZ(3)', 'cockroachdb': 'TIMESTAMPTZ(3)', 'default': 'TIMESTAMPTZ'}, 'datetime64[s]': {'timescaledb': 'TIMESTAMP(0)', 'timescaledb-ha': 'TIMESTAMP(0)', 'postgresql': 'TIMESTAMP(0)', 'postgis': 'TIMESTAMP(0)', 'mariadb': 'DATETIME', 'mysql': 'DATETIME', 'mssql': 'DATETIME2(0)', 'oracle': 'TIMESTAMP(0)', 'sqlite': 'DATETIME', 'geopackage': 'DATETIME', 'duckdb': 'TIMESTAMP(0)', 'citus': 'TIMESTAMP(0)', 'cockroachdb': 'TIMESTAMP(0)', 'default': 'DATETIME'}, 'datetime64[s, UTC]': {'timescaledb': 'TIMESTAMPTZ(0)', 'timescaledb-ha': 'TIMESTAMPTZ(0)', 'postgresql': 'TIMESTAMPTZ(0)', 'postgis': 'TIMESTAMPTZ(0)', 'mariadb': 'DATETIME', 'mysql': 'DATETIME', 'mssql': 'DATETIMEOFFSET(0)', 'oracle': 'TIMESTAMP(0)', 'sqlite': 'TIMESTAMP', 'geopackage': 'TIMESTAMP', 'duckdb': 'TIMESTAMPTZ', 'citus': 'TIMESTAMPTZ(0)', 'cockroachdb': 'TIMESTAMPTZ(0)', 'default': 'TIMESTAMPTZ(0)'}, 'datetime': {'timescaledb': 'TIMESTAMPTZ', 'timescaledb-ha': 'TIMESTAMPTZ', 'postgresql': 'TIMESTAMPTZ', 'postgis': 'TIMESTAMPTZ', 'mariadb': 'DATETIME', 'mysql': 'DATETIME', 'mssql': 'DATETIMEOFFSET', 'oracle': 'TIMESTAMP', 'sqlite': 'TIMESTAMP', 'geopackage': 'TIMESTAMP', 'duckdb': 'TIMESTAMPTZ', 'citus': 'TIMESTAMPTZ', 'cockroachdb': 'TIMESTAMPTZ', 'default': 'TIMESTAMPTZ'}, 'datetimetz': {'timescaledb': 'TIMESTAMPTZ', 'timescaledb-ha': 'TIMESTAMPTZ', 'postgresql': 'TIMESTAMPTZ', 'postgis': 'TIMESTAMPTZ', 'mariadb': 'DATETIME', 'mysql': 'DATETIME', 'mssql': 'DATETIMEOFFSET', 'oracle': 'TIMESTAMP', 'sqlite': 'TIMESTAMP', 'geopackage': 'TIMESTAMP', 'duckdb': 'TIMESTAMPTZ', 'citus': 'TIMESTAMPTZ', 'cockroachdb': 'TIMESTAMPTZ', 'default': 'TIMESTAMPTZ'}, 'date': {'timescaledb': 'DATE', 'timescaledb-ha': 'DATE', 'postgresql': 'DATE', 'postgis': 'DATE', 'mariadb': 'DATE', 'mysql': 'DATE', 'mssql': 'DATE', 'oracle': 'DATE', 'sqlite': 'DATE', 'geopackage': 'DATE', 'duckdb': 'DATE', 'citus': 'DATE', 'cockroachdb': 'DATE', 'default': 'DATE'}, 'bool': {'timescaledb': 'BOOLEAN', 'timescaledb-ha': 'BOOLEAN', 'postgresql': 'BOOLEAN', 'postgis': 'BOOLEAN', 'mariadb': 'BOOLEAN', 'mysql': 'BOOLEAN', 'mssql': 'BIT', 'oracle': 'INTEGER', 'sqlite': 'FLOAT', 'geopackage': 'FLOAT', 'duckdb': 'BOOLEAN', 'citus': 'BOOLEAN', 'cockroachdb': 'BOOLEAN', 'default': 'BOOLEAN'}, 'bool[pyarrow]': {'timescaledb': 'BOOLEAN', 'timescaledb-ha': 'BOOLEAN', 'postgresql': 'BOOLEAN', 'postgis': 'BOOLEAN', 'mariadb': 'BOOLEAN', 'mysql': 'BOOLEAN', 'mssql': 'BIT', 'oracle': 'INTEGER', 'sqlite': 'FLOAT', 'geopackage': 'FLOAT', 'duckdb': 'BOOLEAN', 'citus': 'BOOLEAN', 'cockroachdb': 'BOOLEAN', 'default': 'BOOLEAN'}, 'boolean': {'timescaledb': 'BOOLEAN', 'timescaledb-ha': 'BOOLEAN', 'postgresql': 'BOOLEAN', 'postgis': 'BOOLEAN', 'mariadb': 'BOOLEAN', 'mysql': 'BOOLEAN', 'mssql': 'BIT', 'oracle': 'INTEGER', 'sqlite': 'FLOAT', 'geopackage': 'FLOAT', 'duckdb': 'BOOLEAN', 'citus': 'BOOLEAN', 'cockroachdb': 'BOOLEAN', 'default': 'BOOLEAN'}, 'object': {'timescaledb': 'TEXT', 'timescaledb-ha': 'TEXT', 'postgresql': 'TEXT', 'postgis': 'TEXT', 'mariadb': 'TEXT', 'mysql': 'TEXT', 'mssql': 'NVARCHAR(MAX)', 'oracle': 'NVARCHAR2(2000)', 'sqlite': 'TEXT', 'geopackage': 'TEXT', 'duckdb': 'TEXT', 'citus': 'TEXT', 'cockroachdb': 'TEXT', 'default': 'TEXT'}, 'string': {'timescaledb': 'TEXT', 'timescaledb-ha': 'TEXT', 'postgresql': 'TEXT', 'postgis': 'TEXT', 'mariadb': 'TEXT', 'mysql': 'TEXT', 'mssql': 'NVARCHAR(MAX)', 'oracle': 'NVARCHAR2(2000)', 'sqlite': 'TEXT', 'geopackage': 'TEXT', 'duckdb': 'TEXT', 'citus': 'TEXT', 'cockroachdb': 'TEXT', 'default': 'TEXT'}, 'unicode': {'timescaledb': 'TEXT', 'timescaledb-ha': 'TEXT', 'postgresql': 'TEXT', 'postgis': 'TEXT', 'mariadb': 'TEXT', 'mysql': 'TEXT', 'mssql': 'NVARCHAR(MAX)', 'oracle': 'NVARCHAR2(2000)', 'sqlite': 'TEXT', 'geopackage': 'TEXT', 'duckdb': 'TEXT', 'citus': 'TEXT', 'cockroachdb': 'TEXT', 'default': 'TEXT'}, 'json': {'timescaledb': 'JSONB', 'timescaledb-ha': 'JSONB', 'postgresql': 'JSONB', 'postgis': 'JSONB', 'mariadb': 'TEXT', 'mysql': 'TEXT', 'mssql': 'NVARCHAR(MAX)', 'oracle': 'NVARCHAR2(2000)', 'sqlite': 'TEXT', 'geopackage': 'TEXT', 'duckdb': 'TEXT', 'citus': 'JSONB', 'cockroachdb': 'JSONB', 'default': 'TEXT'}, 'numeric': {'timescaledb': 'NUMERIC', 'timescaledb-ha': 'NUMERIC', 'postgresql': 'NUMERIC', 'postgis': 'NUMERIC', 'mariadb': 'DECIMAL(38, 20)', 'mysql': 'DECIMAL(38, 20)', 'mssql': 'NUMERIC(28, 10)', 'oracle': 'NUMBER', 'sqlite': 'TEXT', 'geopackage': 'TEXT', 'duckdb': 'TEXT', 'citus': 'NUMERIC', 'cockroachdb': 'NUMERIC', 'default': 'NUMERIC'}, 'uuid': {'timescaledb': 'UUID', 'timescaledb-ha': 'UUID', 'postgresql': 'UUID', 'postgis': 'UUID', 'mariadb': 'CHAR(36)', 'mysql': 'CHAR(36)', 'mssql': 'UNIQUEIDENTIFIER', 'oracle': 'CHAR(36)', 'sqlite': 'TEXT', 'geopackage': 'TEXT', 'duckdb': 'VARCHAR', 'citus': 'UUID', 'cockroachdb': 'UUID', 'default': 'TEXT'}, 'bytes': {'timescaledb': 'BYTEA', 'timescaledb-ha': 'BYTEA', 'postgresql': 'BYTEA', 'postgis': 'BYTEA', 'mariadb': 'BLOB', 'mysql': 'BLOB', 'mssql': 'VARBINARY(MAX)', 'oracle': 'BLOB', 'sqlite': 'BLOB', 'geopackage': 'BLOB', 'duckdb': 'BLOB', 'citus': 'BYTEA', 'cockroachdb': 'BYTEA', 'default': 'BLOB'}, 'geometry': {'timescaledb': 'TEXT', 'timescaledb-ha': 'GEOMETRY', 'postgresql': 'TEXT', 'postgis': 'GEOMETRY', 'mariadb': 'TEXT', 'mysql': 'TEXT', 'mssql': 'NVARCHAR(MAX)', 'oracle': 'NVARCHAR2(2000)', 'sqlite': 'TEXT', 'geopackage': 'GEOMETRY', 'duckdb': 'TEXT', 'citus': 'TEXT', 'cockroachdb': 'TEXT', 'default': 'TEXT'}, 'geography': {'timescaledb': 'TEXT', 'timescaledb-ha': 'GEOGRAPHY', 'postgresql': 'TEXT', 'postgis': 'GEOGRAPHY', 'mariadb': 'TEXT', 'mysql': 'TEXT', 'mssql': 'NVARCHAR(MAX)', 'oracle': 'NVARCHAR2(2000)', 'sqlite': 'TEXT', 'geopackage': 'TEXT', 'duckdb': 'TEXT', 'citus': 'TEXT', 'cockroachdb': 'TEXT', 'default': 'TEXT'}}
PD_TO_SQLALCHEMY_DTYPES_FLAVORS: Dict[str, Dict[str, str]] = {'int': {'timescaledb': 'BigInteger', 'timescaledb-ha': 'BigInteger', 'postgresql': 'BigInteger', 'postgis': 'BigInteger', 'mariadb': 'BigInteger', 'mysql': 'BigInteger', 'mssql': 'BigInteger', 'oracle': 'BigInteger', 'sqlite': 'BigInteger', 'geopackage': 'BigInteger', 'duckdb': 'BigInteger', 'citus': 'BigInteger', 'cockroachdb': 'BigInteger', 'default': 'BigInteger'}, 'uint': {'timescaledb': 'BigInteger', 'timescaledb-ha': 'BigInteger', 'postgresql': 'BigInteger', 'postgis': 'BigInteger', 'mariadb': 'BigInteger', 'mysql': 'BigInteger', 'mssql': 'BigInteger', 'oracle': 'BigInteger', 'sqlite': 'BigInteger', 'geopackage': 'BigInteger', 'duckdb': 'BigInteger', 'citus': 'BigInteger', 'cockroachdb': 'BigInteger', 'default': 'BigInteger'}, 'int8': {'timescaledb': 'SmallInteger', 'timescaledb-ha': 'SmallInteger', 'postgresql': 'SmallInteger', 'postgis': 'SmallInteger', 'mariadb': 'SmallInteger', 'mysql': 'SmallInteger', 'mssql': 'SmallInteger', 'oracle': 'SmallInteger', 'sqlite': 'SmallInteger', 'geopackage': 'SmallInteger', 'duckdb': 'SmallInteger', 'citus': 'SmallInteger', 'cockroachdb': 'SmallInteger', 'default': 'SmallInteger'}, 'uint8': {'timescaledb': 'SmallInteger', 'timescaledb-ha': 'SmallInteger', 'postgresql': 'SmallInteger', 'postgis': 'SmallInteger', 'mariadb': 'SmallInteger', 'mysql': 'SmallInteger', 'mssql': 'SmallInteger', 'oracle': 'SmallInteger', 'sqlite': 'SmallInteger', 'geopackage': 'SmallInteger', 'duckdb': 'SmallInteger', 'citus': 'SmallInteger', 'cockroachdb': 'SmallInteger', 'default': 'SmallInteger'}, 'int16': {'timescaledb': 'SmallInteger', 'timescaledb-ha': 'SmallInteger', 'postgresql': 'SmallInteger', 'postgis': 'SmallInteger', 'mariadb': 'SmallInteger', 'mysql': 'SmallInteger', 'mssql': 'SmallInteger', 'oracle': 'SmallInteger', 'sqlite': 'SmallInteger', 'geopackage': 'SmallInteger', 'duckdb': 'SmallInteger', 'citus': 'SmallInteger', 'cockroachdb': 'SmallInteger', 'default': 'SmallInteger'}, 'int32': {'timescaledb': 'Integer', 'timescaledb-ha': 'Integer', 'postgresql': 'Integer', 'postgis': 'Integer', 'mariadb': 'Integer', 'mysql': 'Integer', 'mssql': 'Integer', 'oracle': 'Integer', 'sqlite': 'Integer', 'geopackage': 'Integer', 'duckdb': 'Integer', 'citus': 'Integer', 'cockroachdb': 'Integer', 'default': 'Integer'}, 'int64': {'timescaledb': 'BigInteger', 'timescaledb-ha': 'BigInteger', 'postgresql': 'BigInteger', 'postgis': 'BigInteger', 'mariadb': 'BigInteger', 'mysql': 'BigInteger', 'mssql': 'BigInteger', 'oracle': 'BigInteger', 'sqlite': 'BigInteger', 'geopackage': 'BigInteger', 'duckdb': 'BigInteger', 'citus': 'BigInteger', 'cockroachdb': 'BigInteger', 'default': 'BigInteger'}, 'float': {'timescaledb': 'Float', 'timescaledb-ha': 'Float', 'postgresql': 'Float', 'postgis': 'Float', 'mariadb': 'Float', 'mysql': 'Float', 'mssql': 'Float', 'oracle': 'Float', 'sqlite': 'Float', 'geopackage': 'Float', 'duckdb': 'Float', 'citus': 'Float', 'cockroachdb': 'Float', 'default': 'Float'}, 'datetime': {'timescaledb': 'DateTime(timezone=True)', 'timescaledb-ha': 'DateTime(timezone=True)', 'postgresql': 'DateTime(timezone=True)', 'postgis': 'DateTime(timezone=True)', 'mariadb': 'DateTime(timezone=True)', 'mysql': 'DateTime(timezone=True)', 'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET', 'oracle': 'sqlalchemy.dialects.oracle.TIMESTAMP', 'sqlite': 'DateTime(timezone=True)', 'geopackage': 'DateTime(timezone=True)', 'duckdb': 'DateTime(timezone=True)', 'citus': 'DateTime(timezone=True)', 'cockroachdb': 'DateTime(timezone=True)', 'default': 'DateTime(timezone=True)'}, 'datetime64[ns]': {'timescaledb': 'DateTime', 'timescaledb-ha': 'DateTime', 'postgresql': 'DateTime', 'postgis': 'DateTime', 'mariadb': 'DateTime', 'mysql': 'DateTime', 'mssql': 'sqlalchemy.dialects.mssql.DATETIME2', 'oracle': 'DateTime', 'sqlite': 'DateTime', 'geopackage': 'DateTime', 'duckdb': 'DateTime', 'citus': 'DateTime', 'cockroachdb': 'DateTime', 'default': 'DateTime'}, 'datetime64[ns, UTC]': {'timescaledb': 'DateTime(timezone=True)', 'timescaledb-ha': 'DateTime(timezone=True)', 'postgresql': 'DateTime(timezone=True)', 'postgis': 'DateTime(timezone=True)', 'mariadb': 'DateTime(timezone=True)', 'mysql': 'DateTime(timezone=True)', 'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET', 'oracle': 'sqlalchemy.dialects.oracle.TIMESTAMP', 'sqlite': 'DateTime(timezone=True)', 'geopackage': 'DateTime(timezone=True)', 'duckdb': 'DateTime(timezone=True)', 'citus': 'DateTime(timezone=True)', 'cockroachdb': 'DateTime(timezone=True)', 'default': 'DateTime(timezone=True)'}, 'datetime64[us]': {'timescaledb': 'DateTime', 'timescaledb-ha': 'DateTime', 'postgresql': 'DateTime', 'postgis': 'DateTime', 'mariadb': 'DateTime', 'mysql': 'DateTime', 'mssql': 'sqlalchemy.dialects.mssql.DATETIME2', 'oracle': 'DateTime', 'sqlite': 'DateTime', 'geopackage': 'DateTime', 'duckdb': 'DateTime', 'citus': 'DateTime', 'cockroachdb': 'DateTime', 'default': 'DateTime'}, 'datetime64[us, UTC]': {'timescaledb': 'DateTime(timezone=True)', 'timescaledb-ha': 'DateTime(timezone=True)', 'postgresql': 'DateTime(timezone=True)', 'postgis': 'DateTime(timezone=True)', 'mariadb': 'DateTime(timezone=True)', 'mysql': 'DateTime(timezone=True)', 'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET', 'oracle': 'sqlalchemy.dialects.oracle.TIMESTAMP', 'sqlite': 'DateTime(timezone=True)', 'geopackage': 'DateTime(timezone=True)', 'duckdb': 'DateTime(timezone=True)', 'citus': 'DateTime(timezone=True)', 'cockroachdb': 'DateTime(timezone=True)', 'default': 'DateTime(timezone=True)'}, 'datetime64[ms]': {'timescaledb': 'DateTime', 'timescaledb-ha': 'DateTime', 'postgresql': 'DateTime', 'postgis': 'DateTime', 'mariadb': 'DateTime', 'mysql': 'DateTime', 'mssql': 'sqlalchemy.dialects.mssql.DATETIME2', 'oracle': 'DateTime', 'sqlite': 'DateTime', 'geopackage': 'DateTime', 'duckdb': 'DateTime', 'citus': 'DateTime', 'cockroachdb': 'DateTime', 'default': 'DateTime'}, 'datetime64[ms, UTC]': {'timescaledb': 'DateTime(timezone=True)', 'timescaledb-ha': 'DateTime(timezone=True)', 'postgresql': 'DateTime(timezone=True)', 'postgis': 'DateTime(timezone=True)', 'mariadb': 'DateTime(timezone=True)', 'mysql': 'DateTime(timezone=True)', 'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET', 'oracle': 'sqlalchemy.dialects.oracle.TIMESTAMP', 'sqlite': 'DateTime(timezone=True)', 'geopackage': 'DateTime(timezone=True)', 'duckdb': 'DateTime(timezone=True)', 'citus': 'DateTime(timezone=True)', 'cockroachdb': 'DateTime(timezone=True)', 'default': 'DateTime(timezone=True)'}, 'datetime64[s]': {'timescaledb': 'DateTime', 'timescaledb-ha': 'DateTime', 'postgresql': 'DateTime', 'postgis': 'DateTime', 'mariadb': 'DateTime', 'mysql': 'DateTime', 'mssql': 'sqlalchemy.dialects.mssql.DATETIME2', 'oracle': 'DateTime', 'sqlite': 'DateTime', 'geopackage': 'DateTime', 'duckdb': 'DateTime', 'citus': 'DateTime', 'cockroachdb': 'DateTime', 'default': 'DateTime'}, 'datetime64[s, UTC]': {'timescaledb': 'DateTime(timezone=True)', 'timescaledb-ha': 'DateTime(timezone=True)', 'postgresql': 'DateTime(timezone=True)', 'postgis': 'DateTime(timezone=True)', 'mariadb': 'DateTime(timezone=True)', 'mysql': 'DateTime(timezone=True)', 'mssql': 'sqlalchemy.dialects.mssql.DATETIMEOFFSET', 'oracle': 'sqlalchemy.dialects.oracle.TIMESTAMP', 'sqlite': 'DateTime(timezone=True)', 'geopackage': 'DateTime(timezone=True)', 'duckdb': 'DateTime(timezone=True)', 'citus': 'DateTime(timezone=True)', 'cockroachdb': 'DateTime(timezone=True)', 'default': 'DateTime(timezone=True)'}, 'date': {'timescaledb': 'Date', 'timescaledb-ha': 'Date', 'postgresql': 'Date', 'postgis': 'Date', 'mariadb': 'Date', 'mysql': 'Date', 'mssql': 'Date', 'oracle': 'Date', 'sqlite': 'Date', 'geopackage': 'Date', 'duckdb': 'Date', 'citus': 'Date', 'cockroachdb': 'Date', 'default': 'Date'}, 'bool': {'timescaledb': 'Boolean', 'timescaledb-ha': 'Boolean', 'postgresql': 'Boolean', 'postgis': 'Boolean', 'mariadb': 'Integer', 'mysql': 'Integer', 'mssql': 'sqlalchemy.dialects.mssql.BIT', 'oracle': 'Integer', 'sqlite': 'Float', 'geopackage': 'Float', 'duckdb': 'Boolean', 'citus': 'Boolean', 'cockroachdb': 'Boolean', 'default': 'Boolean'}, 'bool[pyarrow]': {'timescaledb': 'Boolean', 'timescaledb-ha': 'Boolean', 'postgresql': 'Boolean', 'postgis': 'Boolean', 'mariadb': 'Integer', 'mysql': 'Integer', 'mssql': 'sqlalchemy.dialects.mssql.BIT', 'oracle': 'Integer', 'sqlite': 'Float', 'geopackage': 'Float', 'duckdb': 'Boolean', 'citus': 'Boolean', 'cockroachdb': 'Boolean', 'default': 'Boolean'}, 'boolean': {'timescaledb': 'Boolean', 'timescaledb-ha': 'Boolean', 'postgresql': 'Boolean', 'postgis': 'Boolean', 'mariadb': 'Integer', 'mysql': 'Integer', 'mssql': 'sqlalchemy.dialects.mssql.BIT', 'oracle': 'Integer', 'sqlite': 'Float', 'geopackage': 'Float', 'duckdb': 'Boolean', 'citus': 'Boolean', 'cockroachdb': 'Boolean', 'default': 'Boolean'}, 'object': {'timescaledb': 'UnicodeText', 'timescaledb-ha': 'UnicodeText', 'postgresql': 'UnicodeText', 'postgis': 'UnicodeText', 'mariadb': 'UnicodeText', 'mysql': 'UnicodeText', 'mssql': 'UnicodeText', 'oracle': 'UnicodeText', 'sqlite': 'UnicodeText', 'geopackage': 'UnicodeText', 'duckdb': 'UnicodeText', 'citus': 'UnicodeText', 'cockroachdb': 'UnicodeText', 'default': 'UnicodeText'}, 'string': {'timescaledb': 'UnicodeText', 'timescaledb-ha': 'UnicodeText', 'postgresql': 'UnicodeText', 'postgis': 'UnicodeText', 'mariadb': 'UnicodeText', 'mysql': 'UnicodeText', 'mssql': 'UnicodeText', 'oracle': 'UnicodeText', 'sqlite': 'UnicodeText', 'geopackage': 'UnicodeText', 'duckdb': 'UnicodeText', 'citus': 'UnicodeText', 'cockroachdb': 'UnicodeText', 'default': 'UnicodeText'}, 'json': {'timescaledb': 'sqlalchemy.dialects.postgresql.JSONB', 'timescaledb-ha': 'sqlalchemy.dialects.postgresql.JSONB', 'postgresql': 'sqlalchemy.dialects.postgresql.JSONB', 'postgis': 'sqlalchemy.dialects.postgresql.JSONB', 'mariadb': 'UnicodeText', 'mysql': 'UnicodeText', 'mssql': 'UnicodeText', 'oracle': 'UnicodeText', 'sqlite': 'UnicodeText', 'geopackage': 'UnicodeText', 'duckdb': 'TEXT', 'citus': 'sqlalchemy.dialects.postgresql.JSONB', 'cockroachdb': 'sqlalchemy.dialects.postgresql.JSONB', 'default': 'UnicodeText'}, 'numeric': {'timescaledb': 'Numeric', 'timescaledb-ha': 'Numeric', 'postgresql': 'Numeric', 'postgis': 'Numeric', 'mariadb': 'Numeric', 'mysql': 'Numeric', 'mssql': 'Numeric', 'oracle': 'Numeric', 'sqlite': 'UnicodeText', 'geopackage': 'UnicodeText', 'duckdb': 'Numeric', 'citus': 'Numeric', 'cockroachdb': 'Numeric', 'default': 'Numeric'}, 'uuid': {'timescaledb': 'Uuid', 'timescaledb-ha': 'Uuid', 'postgresql': 'Uuid', 'postgis': 'Uuid', 'mariadb': 'sqlalchemy.dialects.mysql.CHAR(36)', 'mysql': 'sqlalchemy.dialects.mysql.CHAR(36)', 'mssql': 'Uuid', 'oracle': 'sqlalchemy.dialects.oracle.CHAR(36)', 'sqlite': 'UnicodeText', 'geopackage': 'UnicodeText', 'duckdb': 'UnicodeText', 'citus': 'Uuid', 'cockroachdb': 'Uuid', 'default': 'Uuid'}, 'binary[pyarrow]': {'timescaledb': 'LargeBinary', 'timescaledb-ha': 'LargeBinary', 'postgresql': 'LargeBinary', 'postgis': 'LargeBinary', 'mariadb': 'LargeBinary', 'mysql': 'LargeBinary', 'mssql': 'LargeBinary', 'oracle': 'LargeBinary', 'sqlite': 'LargeBinary', 'geopackage': 'LargeBinary', 'duckdb': 'LargeBinary', 'citus': 'LargeBinary', 'cockroachdb': 'LargeBinary', 'default': 'LargeBinary'}, 'bytes': {'timescaledb': 'LargeBinary', 'timescaledb-ha': 'LargeBinary', 'postgresql': 'LargeBinary', 'postgis': 'LargeBinary', 'mariadb': 'LargeBinary', 'mysql': 'LargeBinary', 'mssql': 'LargeBinary', 'oracle': 'LargeBinary', 'sqlite': 'LargeBinary', 'geopackage': 'LargeBinary', 'duckdb': 'LargeBinary', 'citus': 'LargeBinary', 'cockroachdb': 'LargeBinary', 'default': 'LargeBinary'}, 'geometry': {'timescaledb': 'UnicodeText', 'timescaledb-ha': 'geoalchemy2.Geometry', 'postgresql': 'UnicodeText', 'postgis': 'geoalchemy2.Geometry', 'mariadb': 'UnicodeText', 'mysql': 'UnicodeText', 'mssql': 'UnicodeText', 'oracle': 'UnicodeText', 'sqlite': 'UnicodeText', 'geopackage': 'LargeBinary', 'duckdb': 'UnicodeText', 'citus': 'UnicodeText', 'cockroachdb': 'UnicodeText', 'default': 'UnicodeText'}, 'geography': {'timescaledb': 'UnicodeText', 'timescaledb-ha': 'geoalchemy2.Geography', 'postgresql': 'UnicodeText', 'postgis': 'geoalchemy2.Geography', 'mariadb': 'UnicodeText', 'mysql': 'UnicodeText', 'mssql': 'UnicodeText', 'oracle': 'UnicodeText', 'sqlite': 'UnicodeText', 'geopackage': 'UnicodeText', 'duckdb': 'UnicodeText', 'citus': 'UnicodeText', 'cockroachdb': 'UnicodeText', 'default': 'UnicodeText'}}
AUTO_INCREMENT_COLUMN_FLAVORS: Dict[str, str] = {'timescaledb': 'GENERATED BY DEFAULT AS IDENTITY', 'timescaledb-ha': 'GENERATED BY DEFAULT AS IDENTITY', 'postgresql': 'GENERATED BY DEFAULT AS IDENTITY', 'postgis': 'GENERATED BY DEFAULT AS IDENTITY', 'mariadb': 'AUTO_INCREMENT', 'mysql': 'AUTO_INCREMENT', 'mssql': 'IDENTITY(1,1)', 'oracle': 'GENERATED BY DEFAULT ON NULL AS IDENTITY', 'sqlite': 'AUTOINCREMENT', 'geopackage': 'AUTOINCREMENT', 'duckdb': 'GENERATED BY DEFAULT', 'citus': 'GENERATED BY DEFAULT', 'cockroachdb': 'GENERATED BY DEFAULT AS IDENTITY', 'default': 'GENERATED BY DEFAULT AS IDENTITY'}
def get_pd_type_from_db_type(db_type: str, allow_custom_dtypes: bool = True) -> str:
1163def get_pd_type_from_db_type(db_type: str, allow_custom_dtypes: bool = True) -> str:
1164    """
1165    Parse a database type to a pandas data type.
1166
1167    Parameters
1168    ----------
1169    db_type: str
1170        The database type, e.g. `DATETIME`, `BIGINT`, etc.
1171
1172    allow_custom_dtypes: bool, default False
1173        If `True`, allow for custom data types like `json` and `str`.
1174
1175    Returns
1176    -------
1177    The equivalent datatype for a pandas DataFrame.
1178    """
1179    from meerschaum.utils.dtypes import are_dtypes_equal, get_geometry_type_srid
1180    def parse_custom(_pd_type: str, _db_type: str) -> str:
1181        if 'json' in _db_type.lower():
1182            return 'json'
1183        if are_dtypes_equal(_pd_type, 'numeric') and _pd_type != 'object':
1184            precision, scale = get_numeric_precision_scale(None, dtype=_db_type.upper())
1185            if precision and scale:
1186                return f"numeric[{precision},{scale}]"
1187        if are_dtypes_equal(_pd_type, 'geometry') and _pd_type != 'object':
1188            geometry_type, srid = get_geometry_type_srid(_db_type.upper())
1189            modifiers = [str(modifier) for modifier in (geometry_type, srid) if modifier]
1190            typ = "geometry" if 'geography' not in _pd_type.lower() else 'geography'
1191            if not modifiers:
1192                return typ
1193            return f"{typ}[{', '.join(modifiers)}]"
1194        return _pd_type
1195
1196    pd_type = DB_TO_PD_DTYPES.get(db_type.upper().split('(', maxsplit=1)[0].strip(), None)
1197    if pd_type is not None:
1198        return (
1199            parse_custom(pd_type, db_type)
1200            if allow_custom_dtypes
1201            else pd_type
1202        )
1203    for db_t, pd_t in DB_TO_PD_DTYPES['substrings'].items():
1204        if db_t in db_type.upper():
1205            return (
1206                parse_custom(pd_t, db_t)
1207                if allow_custom_dtypes
1208                else pd_t
1209            )
1210    return DB_TO_PD_DTYPES['default']

Parse a database type to a pandas data type.

Parameters
  • db_type (str): The database type, e.g. DATETIME, BIGINT, etc.
  • allow_custom_dtypes (bool, default False): If True, allow for custom data types like json and str.
Returns
  • The equivalent datatype for a pandas DataFrame.
def get_db_type_from_pd_type( pd_type: str, flavor: str = 'default', as_sqlalchemy: bool = False) -> "Union[str, 'sqlalchemy.sql.visitors.TraversibleType']":
1213def get_db_type_from_pd_type(
1214    pd_type: str,
1215    flavor: str = 'default',
1216    as_sqlalchemy: bool = False,
1217) -> Union[str, 'sqlalchemy.sql.visitors.TraversibleType']:
1218    """
1219    Parse a Pandas data type into a flavor's database type.
1220
1221    Parameters
1222    ----------
1223    pd_type: str
1224        The Pandas datatype. This must be a string, not the actual dtype object.
1225
1226    flavor: str, default 'default'
1227        The flavor of the database to be mapped to.
1228
1229    as_sqlalchemy: bool, default False
1230        If `True`, return a type from `sqlalchemy.types`.
1231
1232    Returns
1233    -------
1234    The database data type for the incoming Pandas data type.
1235    If nothing can be found, a warning will be thrown and 'TEXT' will be returned.
1236    """
1237    from meerschaum.utils.warnings import warn
1238    from meerschaum.utils.packages import attempt_import
1239    from meerschaum.utils.dtypes import are_dtypes_equal, MRSM_ALIAS_DTYPES, get_geometry_type_srid
1240    from meerschaum.utils.misc import parse_arguments_str
1241    sqlalchemy_types = attempt_import('sqlalchemy.types', lazy=False)
1242
1243    types_registry = (
1244        PD_TO_DB_DTYPES_FLAVORS
1245        if not as_sqlalchemy
1246        else PD_TO_SQLALCHEMY_DTYPES_FLAVORS
1247    )
1248
1249    precision, scale = None, None
1250    geometry_type, geometry_srid = None, None
1251    og_pd_type = pd_type
1252    if pd_type in MRSM_ALIAS_DTYPES:
1253        pd_type = MRSM_ALIAS_DTYPES[pd_type]
1254
1255    ### Check whether we are able to match this type (e.g. pyarrow support).
1256    found_db_type = False
1257    if (
1258        pd_type not in types_registry
1259        and not any(
1260            pd_type.startswith(f'{typ}[')
1261            for typ in ('numeric', 'geometry', 'geography')
1262        )
1263    ):
1264        for mapped_pd_type in types_registry:
1265            if are_dtypes_equal(mapped_pd_type, pd_type):
1266                pd_type = mapped_pd_type
1267                found_db_type = True
1268                break
1269    elif (pd_type.startswith('geometry[') or pd_type.startswith('geography[')):
1270        og_pd_type = pd_type
1271        pd_type = 'geometry' if 'geometry' in pd_type else 'geography'
1272        geometry_type, geometry_srid = get_geometry_type_srid(og_pd_type)
1273        found_db_type = True
1274    elif pd_type.startswith('numeric['):
1275        og_pd_type = pd_type
1276        pd_type = 'numeric'
1277        precision, scale = get_numeric_precision_scale(flavor, og_pd_type)
1278        found_db_type = True
1279    else:
1280        found_db_type = True
1281
1282    if not found_db_type:
1283        warn(f"Unknown Pandas data type '{pd_type}'. Falling back to 'TEXT'.", stacklevel=3)
1284        return (
1285            'TEXT'
1286            if not as_sqlalchemy
1287            else sqlalchemy_types.UnicodeText
1288        )
1289    flavor_types = types_registry.get(
1290        pd_type,
1291        {
1292            'default': (
1293                'TEXT'
1294                if not as_sqlalchemy
1295                else 'UnicodeText'
1296            ),
1297        },
1298    )
1299    default_flavor_type = flavor_types.get(
1300        'default',
1301        (
1302            'TEXT'
1303            if not as_sqlalchemy
1304            else 'UnicodeText'
1305        ),
1306    )
1307    if flavor not in flavor_types:
1308        warn(f"Unknown flavor '{flavor}'. Falling back to '{default_flavor_type}' (default).")
1309    db_type = flavor_types.get(flavor, default_flavor_type)
1310    if not as_sqlalchemy:
1311        if precision is not None and scale is not None:
1312            db_type_bare = db_type.split('(', maxsplit=1)[0]
1313            return f"{db_type_bare}({precision},{scale})"
1314        if geometry_type is not None and geometry_srid is not None:
1315            if 'geometry' not in db_type.lower() and 'geography' not in db_type.lower():
1316                return db_type
1317            db_type_bare = db_type.split('(', maxsplit=1)[0]
1318            return f"{db_type_bare}({geometry_type.upper()}, {geometry_srid})"
1319        return db_type
1320
1321    if db_type.startswith('sqlalchemy.dialects'):
1322        dialect, typ_class_name = db_type.replace('sqlalchemy.dialects.', '').split('.', maxsplit=2)
1323        cls_args, cls_kwargs = None, None
1324        if '(' in typ_class_name:
1325            typ_class_name, args_str = typ_class_name.split('(', maxsplit=1)
1326            args_str = args_str.rstrip(')')
1327            cls_args, cls_kwargs = parse_arguments_str(args_str)
1328        sqlalchemy_dialects_flavor_module = attempt_import(f'sqlalchemy.dialects.{dialect}')
1329        cls = getattr(sqlalchemy_dialects_flavor_module, typ_class_name)
1330        if cls_args is None:
1331            return cls
1332        return cls(*cls_args, **cls_kwargs)
1333
1334    if 'geometry' in db_type.lower() or 'geography' in db_type.lower():
1335        geoalchemy2 = attempt_import('geoalchemy2', lazy=False)
1336        geometry_class = (
1337            geoalchemy2.Geometry
1338            if 'geometry' in db_type.lower()
1339            else geoalchemy2.Geography
1340        )
1341        if geometry_type is None or geometry_srid is None:
1342            return geometry_class
1343        return geometry_class(geometry_type=geometry_type, srid=geometry_srid)
1344
1345    if 'numeric' in db_type.lower():
1346        if precision is None or scale is None:
1347            return sqlalchemy_types.Numeric
1348        return sqlalchemy_types.Numeric(precision, scale)
1349
1350    cls_args, cls_kwargs = None, None
1351    typ_class_name = db_type
1352    if '(' in db_type:
1353        typ_class_name, args_str = db_type.split('(', maxsplit=1)
1354        args_str = args_str.rstrip(')')
1355        cls_args, cls_kwargs = parse_arguments_str(args_str)
1356
1357    cls = getattr(sqlalchemy_types, typ_class_name)
1358    if cls_args is None:
1359        return cls
1360    return cls(*cls_args, **cls_kwargs)

Parse a Pandas data type into a flavor's database type.

Parameters
  • pd_type (str): The Pandas datatype. This must be a string, not the actual dtype object.
  • flavor (str, default 'default'): The flavor of the database to be mapped to.
  • as_sqlalchemy (bool, default False): If True, return a type from sqlalchemy.types.
Returns
  • The database data type for the incoming Pandas data type.
  • If nothing can be found, a warning will be thrown and 'TEXT' will be returned.
def get_numeric_precision_scale( flavor: str, dtype: Optional[str] = None) -> Union[Tuple[int, int], Tuple[NoneType, NoneType]]:
1363def get_numeric_precision_scale(
1364    flavor: str,
1365    dtype: Optional[str] = None,
1366) -> Union[Tuple[int, int], Tuple[None, None]]:
1367    """
1368    Return the precision and scale to use for a numeric column for a given database flavor.
1369
1370    Parameters
1371    ----------
1372    flavor: str
1373        The database flavor for which to return the precision and scale.
1374    
1375    dtype: Optional[str], default None
1376        If provided, return the precision and scale provided in the dtype (if applicable).
1377        If all caps, treat this as a DB type.
1378
1379    Returns
1380    -------
1381    A tuple of ints or a tuple of Nones.
1382    """
1383    if not dtype:
1384        return None, None
1385
1386    lbracket = '[' if '[' in dtype else '('
1387    rbracket = ']' if lbracket == '[' else ')'
1388    if lbracket in dtype and dtype.count(',') == 1 and dtype.endswith(rbracket):
1389        try:
1390            parts = dtype.split(lbracket, maxsplit=1)[-1].rstrip(rbracket).split(',', maxsplit=1)
1391            return int(parts[0].strip()), int(parts[1].strip())
1392        except Exception:
1393            pass
1394
1395    return NUMERIC_PRECISION_FLAVORS.get(flavor, (None, None))

Return the precision and scale to use for a numeric column for a given database flavor.

Parameters
  • flavor (str): The database flavor for which to return the precision and scale.
  • dtype (Optional[str], default None): If provided, return the precision and scale provided in the dtype (if applicable). If all caps, treat this as a DB type.
Returns
  • A tuple of ints or a tuple of Nones.