|
| 1 | +importenum |
| 2 | + |
| 3 | +importpytest |
| 4 | +fromsqlalchemy.typesimport ( |
| 5 | +BigInteger, |
| 6 | +Boolean, |
| 7 | +Date, |
| 8 | +DateTime, |
| 9 | +Double, |
| 10 | +Enum, |
| 11 | +Float, |
| 12 | +Integer, |
| 13 | +Interval, |
| 14 | +LargeBinary, |
| 15 | +MatchType, |
| 16 | +Numeric, |
| 17 | +PickleType, |
| 18 | +SchemaType, |
| 19 | +SmallInteger, |
| 20 | +String, |
| 21 | +Text, |
| 22 | +Time, |
| 23 | +TypeEngine, |
| 24 | +Unicode, |
| 25 | +UnicodeText, |
| 26 | +Uuid, |
| 27 | +) |
| 28 | + |
| 29 | +fromdatabricks.sqlalchemyimportDatabricksDialect |
| 30 | + |
| 31 | + |
| 32 | +classDatabricksDataType(enum.Enum): |
| 33 | +"""https://docs.databricks.com/en/sql/language-manual/sql-ref-datatypes.html""" |
| 34 | + |
| 35 | +BIGINT=enum.auto() |
| 36 | +BINARY=enum.auto() |
| 37 | +BOOLEAN=enum.auto() |
| 38 | +DATE=enum.auto() |
| 39 | +DECIMAL=enum.auto() |
| 40 | +DOUBLE=enum.auto() |
| 41 | +FLOAT=enum.auto() |
| 42 | +INT=enum.auto() |
| 43 | +INTERVAL=enum.auto() |
| 44 | +VOID=enum.auto() |
| 45 | +SMALLINT=enum.auto() |
| 46 | +STRING=enum.auto() |
| 47 | +TIMESTAMP=enum.auto() |
| 48 | +TIMESTAMP_NTZ=enum.auto() |
| 49 | +TINYINT=enum.auto() |
| 50 | +ARRAY=enum.auto() |
| 51 | +MAP=enum.auto() |
| 52 | +STRUCT=enum.auto() |
| 53 | + |
| 54 | + |
| 55 | +# Defines the way that SQLAlchemy CamelCase types are compiled into Databricks SQL types. |
| 56 | +# Note: I wish I could define this within the TestCamelCaseTypesCompilation class, but pytest doesn't like that. |
| 57 | +camel_case_type_map= { |
| 58 | +BigInteger:DatabricksDataType.BIGINT, |
| 59 | +LargeBinary:DatabricksDataType.BINARY, |
| 60 | +Boolean:DatabricksDataType.BOOLEAN, |
| 61 | +Date:DatabricksDataType.DATE, |
| 62 | +DateTime:DatabricksDataType.TIMESTAMP, |
| 63 | +Double:DatabricksDataType.DOUBLE, |
| 64 | +Enum:DatabricksDataType.STRING, |
| 65 | +Float:DatabricksDataType.FLOAT, |
| 66 | +Integer:DatabricksDataType.INT, |
| 67 | +Interval:DatabricksDataType.TIMESTAMP, |
| 68 | +Numeric:DatabricksDataType.DECIMAL, |
| 69 | +PickleType:DatabricksDataType.BINARY, |
| 70 | +SmallInteger:DatabricksDataType.SMALLINT, |
| 71 | +String:DatabricksDataType.STRING, |
| 72 | +Text:DatabricksDataType.STRING, |
| 73 | +Time:DatabricksDataType.STRING, |
| 74 | +Unicode:DatabricksDataType.STRING, |
| 75 | +UnicodeText:DatabricksDataType.STRING, |
| 76 | +Uuid:DatabricksDataType.STRING, |
| 77 | +} |
| 78 | + |
| 79 | +# Convert the dictionary into a list of tuples for use in pytest.mark.parametrize |
| 80 | +_as_tuple_list= [(key,value)forkey,valueincamel_case_type_map.items()] |
| 81 | + |
| 82 | + |
| 83 | +classCompilationTestBase: |
| 84 | +dialect=DatabricksDialect() |
| 85 | + |
| 86 | +def_assert_compiled_value(self,type_:TypeEngine,expected:DatabricksDataType): |
| 87 | +"""Assert that when type_ is compiled for the databricks dialect, it renders the DatabricksDataType name. |
| 88 | +
|
| 89 | + This method initialises the type_ with no arguments. |
| 90 | + """ |
| 91 | +compiled_result=type_().compile(dialect=self.dialect)# type: ignore |
| 92 | +assertcompiled_result==expected.name |
| 93 | + |
| 94 | +def_assert_compiled_value_explicit(self,type_:TypeEngine,expected:str): |
| 95 | +"""Assert that when type_ is compiled for the databricks dialect, it renders the expected string. |
| 96 | +
|
| 97 | + This method expects an initialised type_ so that we can test how a TypeEngine created with arguments |
| 98 | + is compiled. |
| 99 | + """ |
| 100 | +compiled_result=type_.compile(dialect=self.dialect) |
| 101 | +assertcompiled_result==expected |
| 102 | + |
| 103 | + |
| 104 | +classTestCamelCaseTypesCompilation(CompilationTestBase): |
| 105 | +"""Per the sqlalchemy documentation[^1] here, the camel case members of sqlalchemy.types are |
| 106 | + are expected to work across all dialects. These tests verify that the types compile into valid |
| 107 | + Databricks SQL type strings. For example, the sqlalchemy.types.Integer() should compile as "INT". |
| 108 | +
|
| 109 | + Truly custom types like STRUCT (notice the uppercase) are not expected to work across all dialects. |
| 110 | + We test these separately. |
| 111 | +
|
| 112 | + Note that these tests have to do with type **name** compiliation. Which is separate from actually |
| 113 | + mapping values between Python and Databricks. |
| 114 | +
|
| 115 | + Note: SchemaType and MatchType are not tested because it's not used in table definitions |
| 116 | +
|
| 117 | + [1]: https://docs.sqlalchemy.org/en/20/core/type_basics.html#generic-camelcase-types |
| 118 | + """ |
| 119 | + |
| 120 | +@pytest.mark.parametrize("type_, expected",_as_tuple_list) |
| 121 | +deftest_bare_camel_case_types_compile(self,type_,expected): |
| 122 | +self._assert_compiled_value(type_,expected) |
| 123 | + |
| 124 | +deftest_numeric_renders_as_decimal_with_precision(self): |
| 125 | +self._assert_compiled_value_explicit(Numeric(10),"DECIMAL(10)") |
| 126 | + |
| 127 | +deftest_numeric_renders_as_decimal_with_precision_and_scale(self): |
| 128 | +self._assert_compiled_value_explicit(Numeric(10,2),"DECIMAL(10, 2)") |