Coverage for python/felis/db/sqltypes.py: 63%

67 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-12 10:48 -0700

1# This file is part of felis. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22import builtins 

23from collections.abc import Mapping 

24from typing import Any, Union 

25 

26from sqlalchemy import Float, SmallInteger, types 

27from sqlalchemy.dialects import mysql, oracle, postgresql 

28from sqlalchemy.ext.compiler import compiles 

29 

30MYSQL = "mysql" 

31ORACLE = "oracle" 

32POSTGRES = "postgresql" 

33SQLITE = "sqlite" 

34 

35 

36class TINYINT(SmallInteger): 

37 """The non-standard TINYINT type.""" 

38 

39 __visit_name__ = "TINYINT" 

40 

41 

42class DOUBLE(Float): 

43 """The non-standard DOUBLE type.""" 

44 

45 __visit_name__ = "DOUBLE" 

46 

47 

48@compiles(TINYINT) 

49def compile_tinyint(type_: Any, compiler: Any, **kw: Any) -> str: 

50 return "TINYINT" 

51 

52 

53@compiles(DOUBLE) 

54def compile_double(type_: Any, compiler: Any, **kw: Any) -> str: 

55 return "DOUBLE" 

56 

57 

58_TypeMap = Mapping[str, Union[types.TypeEngine, type[types.TypeEngine]]] 

59 

60boolean_map: _TypeMap = {MYSQL: mysql.BIT(1), ORACLE: oracle.NUMBER(1), POSTGRES: postgresql.BOOLEAN()} 

61 

62byte_map: _TypeMap = { 

63 MYSQL: mysql.TINYINT(), 

64 ORACLE: oracle.NUMBER(3), 

65 POSTGRES: postgresql.SMALLINT(), 

66} 

67 

68short_map: _TypeMap = { 

69 MYSQL: mysql.SMALLINT(), 

70 ORACLE: oracle.NUMBER(5), 

71 POSTGRES: postgresql.SMALLINT(), 

72} 

73 

74# Skip Oracle 

75int_map: _TypeMap = { 

76 MYSQL: mysql.INTEGER(), 

77 POSTGRES: postgresql.INTEGER(), 

78} 

79 

80long_map: _TypeMap = { 

81 MYSQL: mysql.BIGINT(), 

82 ORACLE: oracle.NUMBER(38, 0), 

83 POSTGRES: postgresql.BIGINT(), 

84} 

85 

86float_map: _TypeMap = { 

87 MYSQL: mysql.FLOAT(), 

88 ORACLE: oracle.BINARY_FLOAT(), 

89 POSTGRES: postgresql.FLOAT(), 

90} 

91 

92double_map: _TypeMap = { 

93 MYSQL: mysql.DOUBLE(), 

94 ORACLE: oracle.BINARY_DOUBLE(), 

95 POSTGRES: postgresql.DOUBLE_PRECISION(), 

96} 

97 

98char_map: _TypeMap = { 

99 MYSQL: mysql.CHAR, 

100 ORACLE: oracle.CHAR, 

101 POSTGRES: postgresql.CHAR, 

102} 

103 

104string_map: _TypeMap = { 

105 MYSQL: mysql.VARCHAR, 

106 ORACLE: oracle.VARCHAR2, 

107 POSTGRES: postgresql.VARCHAR, 

108} 

109 

110unicode_map: _TypeMap = { 

111 MYSQL: mysql.NVARCHAR, 

112 ORACLE: oracle.NVARCHAR2, 

113 POSTGRES: postgresql.VARCHAR, 

114} 

115 

116text_map: _TypeMap = { 

117 MYSQL: mysql.LONGTEXT, 

118 ORACLE: oracle.CLOB, 

119 POSTGRES: postgresql.TEXT, 

120} 

121 

122binary_map: _TypeMap = { 

123 MYSQL: mysql.LONGBLOB, 

124 ORACLE: oracle.BLOB, 

125 POSTGRES: postgresql.BYTEA, 

126} 

127 

128 

129def boolean(**kwargs: Any) -> types.TypeEngine: 

130 return _vary(types.BOOLEAN(), boolean_map, kwargs) 

131 

132 

133def byte(**kwargs: Any) -> types.TypeEngine: 

134 return _vary(TINYINT(), byte_map, kwargs) 

135 

136 

137def short(**kwargs: Any) -> types.TypeEngine: 

138 return _vary(types.SMALLINT(), short_map, kwargs) 

139 

140 

141def int(**kwargs: Any) -> types.TypeEngine: 

142 return _vary(types.INTEGER(), int_map, kwargs) 

143 

144 

145def long(**kwargs: Any) -> types.TypeEngine: 

146 return _vary(types.BIGINT(), long_map, kwargs) 

147 

148 

149def float(**kwargs: Any) -> types.TypeEngine: 

150 return _vary(types.FLOAT(), float_map, kwargs) 

151 

152 

153def double(**kwargs: Any) -> types.TypeEngine: 

154 return _vary(DOUBLE(), double_map, kwargs) 

155 

156 

157def char(length: builtins.int, **kwargs: Any) -> types.TypeEngine: 

158 return _vary(types.CHAR(length), char_map, kwargs, length) 

159 

160 

161def string(length: builtins.int, **kwargs: Any) -> types.TypeEngine: 

162 return _vary(types.VARCHAR(length), string_map, kwargs, length) 

163 

164 

165def unicode(length: builtins.int, **kwargs: Any) -> types.TypeEngine: 

166 return _vary(types.NVARCHAR(length), unicode_map, kwargs, length) 

167 

168 

169def text(length: builtins.int, **kwargs: Any) -> types.TypeEngine: 

170 return _vary(types.CLOB(length), text_map, kwargs, length) 

171 

172 

173def binary(length: builtins.int, **kwargs: Any) -> types.TypeEngine: 

174 return _vary(types.BLOB(length), binary_map, kwargs, length) 

175 

176 

177def timestamp(**kwargs: Any) -> types.TypeEngine: 

178 return types.TIMESTAMP() 

179 

180 

181def _vary( 

182 type_: types.TypeEngine, 

183 variant_map: _TypeMap, 

184 overrides: _TypeMap, 

185 *args: Any, 

186) -> types.TypeEngine: 

187 variants: dict[str, Union[types.TypeEngine, type[types.TypeEngine]]] = dict(variant_map) 

188 variants.update(overrides) 

189 for dialect, variant in variants.items(): 

190 # If this is a class and not an instance, instantiate 

191 if isinstance(variant, type): 

192 variant = variant(*args) 

193 type_ = type_.with_variant(variant, dialect) 

194 return type_