Skip to content

Commit 02c1b52

Browse files
committed
feat(datatype): Added support for IntervalY2M and IntervalD2S
1 parent 5ace622 commit 02c1b52

File tree

8 files changed

+262
-12
lines changed

8 files changed

+262
-12
lines changed

redshift_connector/cursor.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,8 @@ def __build_local_schema_columns_query(
12261226
"when 'super' THEN -16 "
12271227
"when 'varbyte' THEN -4 "
12281228
"when 'geography' THEN -4 "
1229+
"when 'intervaly2m' THEN 1111 "
1230+
"when 'intervald2s' THEN 1111 "
12291231
"else 1111 END as SMALLINT) AS DATA_TYPE, "
12301232
"t.typname as TYPE_NAME, "
12311233
"case typname "
@@ -1264,6 +1266,8 @@ def __build_local_schema_columns_query(
12641266
"when 'super' THEN NULL "
12651267
"when 'varbyte' THEN NULL "
12661268
"when 'geography' THEN NULL "
1269+
"when 'intervaly2m' THEN 32 "
1270+
"when 'intervald2s' THEN 64 "
12671271
"else 2147483647 end as COLUMN_SIZE , "
12681272
"null as BUFFER_LENGTH , "
12691273
"case typname "
@@ -1275,6 +1279,8 @@ def __build_local_schema_columns_query(
12751279
"when 'super' then NULL "
12761280
"when 'varbyte' then NULL "
12771281
"when 'geography' then NULL "
1282+
"when 'intervaly2m' then 32 "
1283+
"when 'intervald2s' then 64 "
12781284
"else 0 end as DECIMAL_DIGITS, "
12791285
"10 AS NUM_PREC_RADIX , "
12801286
"case a.attnotnull OR (t.typtype = 'd' AND t.typnotnull) "
@@ -1328,6 +1334,8 @@ def __build_local_schema_columns_query(
13281334
"when 'super' THEN -16 "
13291335
"when 'varbyte' THEN -4 "
13301336
"when 'geography' THEN -4 "
1337+
"when 'intervaly2m' THEN 1111 "
1338+
"when 'intervald2s' THEN 1111 "
13311339
"else 1111 END as SMALLINT) AS SQL_DATA_TYPE, "
13321340
"CAST(NULL AS SMALLINT) as SQL_DATETIME_SUB , "
13331341
"case typname "
@@ -1366,6 +1374,8 @@ def __build_local_schema_columns_query(
13661374
"when 'super' THEN NULL "
13671375
"when 'varbyte' THEN NULL "
13681376
"when 'geography' THEN NULL "
1377+
"when 'intervaly2m' THEN 32 "
1378+
"when 'intervald2s' THEN 64 "
13691379
"else 2147483647 end as CHAR_OCTET_LENGTH , "
13701380
"a.attnum AS ORDINAL_POSITION, "
13711381
"case a.attnotnull OR (t.typtype = 'd' AND t.typnotnull) "
@@ -1448,6 +1458,8 @@ def __build_local_schema_columns_query(
14481458
"WHEN 'super' THEN -16 "
14491459
"WHEN 'varbyte' THEN -4 "
14501460
"WHEN 'geography' THEN -4"
1461+
"WHEN 'intervaly2m' THEN 1111 "
1462+
"WHEN 'intervald2s' THEN 1111 "
14511463
"ELSE 1111 END AS SMALLINT) AS DATA_TYPE, "
14521464
"COALESCE(NULL,CASE columntype WHEN 'boolean' THEN 'bool' "
14531465
"WHEN 'character varying' THEN 'varchar' "
@@ -1499,6 +1511,8 @@ def __build_local_schema_columns_query(
14991511
"WHEN 'super' THEN NULL "
15001512
"WHEN 'varbyte' THEN NULL "
15011513
"WHEN 'geography' THEN NULL "
1514+
"WHEN 'intervaly2m' THEN 32 "
1515+
"WHEN 'intervald2s' THEN 64 "
15021516
"ELSE 2147483647 END AS COLUMN_SIZE, "
15031517
"NULL AS BUFFER_LENGTH, "
15041518
"CASE REGEXP_REPLACE(columntype,'[()0-9,]') "
@@ -1513,6 +1527,8 @@ def __build_local_schema_columns_query(
15131527
"WHEN 'numeric' THEN regexp_substr (columntype,'[0-9]+',charindex (',',columntype))::INTEGER "
15141528
"WHEN 'varbyte' THEN NULL "
15151529
"WHEN 'geography' THEN NULL "
1530+
"WHEN 'intervaly2m' THEN 32 "
1531+
"WHEN 'intervald2s' THEN 64 "
15161532
"ELSE 0 END AS DECIMAL_DIGITS, 10 AS NUM_PREC_RADIX, "
15171533
"NULL AS NULLABLE, NULL AS REMARKS, NULL AS COLUMN_DEF, "
15181534
"CAST(CASE columntype_rep "
@@ -1555,6 +1571,8 @@ def __build_local_schema_columns_query(
15551571
"WHEN 'super' THEN -16 "
15561572
"WHEN 'varbyte' THEN -4 "
15571573
"WHEN 'geography' THEN -4 "
1574+
"WHEN 'intervaly2m' THEN 1111 "
1575+
"WHEN 'intervald2s' THEN 1111 "
15581576
"ELSE 1111 END AS SMALLINT) AS SQL_DATA_TYPE, "
15591577
"CAST(NULL AS SMALLINT) AS SQL_DATETIME_SUB, CASE "
15601578
"WHEN LEFT (columntype,7) = 'varchar' THEN regexp_substr (columntype,'[0-9]+',7)::INTEGER "
@@ -1653,6 +1671,8 @@ def __build_universal_schema_columns_query(
16531671
" WHEN 'super' THEN -16 "
16541672
" WHEN 'varbyte' THEN -4 "
16551673
" WHEN 'geography' THEN -4 "
1674+
" WHEN 'intervaly2m' THEN 1111 "
1675+
" WHEN 'intervald2s' THEN 1111 "
16561676
" ELSE 1111 END AS SMALLINT) AS DATA_TYPE,"
16571677
" COALESCE("
16581678
" domain_name,"
@@ -1708,6 +1728,8 @@ def __build_universal_schema_columns_query(
17081728
" WHEN 'super' THEN NULL"
17091729
" WHEN 'varbyte' THEN NULL"
17101730
" WHEN 'geography' THEN NULL "
1731+
" WHEN 'intervaly2m' THEN 32 "
1732+
" WHEN 'intervald2s' THEN 64 "
17111733
" ELSE {unknown_column_size}"
17121734
" END AS COLUMN_SIZE,"
17131735
" NULL AS BUFFER_LENGTH,"
@@ -1723,6 +1745,8 @@ def __build_universal_schema_columns_query(
17231745
" WHEN 'super' THEN NULL"
17241746
" WHEN 'varbyte' THEN NULL"
17251747
" WHEN 'geography' THEN NULL "
1748+
" WHEN 'intervaly2m' THEN 32 "
1749+
" WHEN 'intervald2s' THEN 64 "
17261750
" ELSE 0"
17271751
" END AS DECIMAL_DIGITS,"
17281752
" 10 AS NUM_PREC_RADIX,"
@@ -1771,6 +1795,8 @@ def __build_universal_schema_columns_query(
17711795
" WHEN 'super' THEN -16"
17721796
" WHEN 'varbyte' THEN -4"
17731797
" WHEN 'geography' THEN -4 "
1798+
" WHEN 'intervaly2m' THEN 1111 "
1799+
" WHEN 'intervald2s' THEN 1111 "
17741800
" ELSE 1111 END AS SMALLINT) AS SQL_DATA_TYPE,"
17751801
" CAST(NULL AS SMALLINT) AS SQL_DATETIME_SUB,"
17761802
" CASE data_type"
@@ -1812,6 +1838,8 @@ def __build_universal_schema_columns_query(
18121838
" WHEN 'super' THEN NULL"
18131839
" WHEN 'varbyte' THEN NULL"
18141840
" WHEN 'geography' THEN NULL "
1841+
" WHEN 'intervaly2m' THEN 32 "
1842+
" WHEN 'intervald2s' THEN 64 "
18151843
" ELSE {unknown_column_size}"
18161844
" END AS CHAR_OCTET_LENGTH,"
18171845
" ordinal_position AS ORDINAL_POSITION,"
@@ -1891,6 +1919,8 @@ def __build_universal_all_schema_columns_query(
18911919
" WHEN 'super' THEN -16 "
18921920
" WHEN 'varbyte' THEN -4 "
18931921
" WHEN 'geography' THEN -4 "
1922+
" WHEN 'intervaly2m' THEN 1111 "
1923+
" WHEN 'intervald2s' THEN 1111 "
18941924
" ELSE 1111 END AS SMALLINT) AS DATA_TYPE, "
18951925
" CASE data_type "
18961926
" WHEN 'boolean' THEN 'bool' "
@@ -1944,6 +1974,8 @@ def __build_universal_all_schema_columns_query(
19441974
" WHEN 'super' THEN NULL "
19451975
" WHEN 'varbyte' THEN NULL "
19461976
" WHEN 'geography' THEN NULL "
1977+
" WHEN 'intervaly2m' THEN 32 "
1978+
" WHEN 'intervald2s' THEN 64 "
19471979
" ELSE 2147483647 "
19481980
" END AS COLUMN_SIZE, "
19491981
" NULL AS BUFFER_LENGTH, "
@@ -1959,6 +1991,8 @@ def __build_universal_all_schema_columns_query(
19591991
" WHEN 'super' THEN NULL "
19601992
" WHEN 'varbyte' THEN NULL "
19611993
" WHEN 'geography' THEN NULL "
1994+
" WHEN 'intervaly2m' THEN 32 "
1995+
" WHEN 'intervald2s' THEN 64 "
19621996
" ELSE 0 "
19631997
" END AS DECIMAL_DIGITS, "
19641998
" 10 AS NUM_PREC_RADIX, "
@@ -2007,6 +2041,8 @@ def __build_universal_all_schema_columns_query(
20072041
" WHEN 'super' THEN -16 "
20082042
" WHEN 'varbyte' THEN -4 "
20092043
" WHEN 'geography' THEN -4 "
2044+
" WHEN 'intervaly2m' THEN 1111 "
2045+
" WHEN 'intervald2s' THEN 1111 "
20102046
" ELSE 1111 END AS SMALLINT) AS SQL_DATA_TYPE, "
20112047
" CAST(NULL AS SMALLINT) AS SQL_DATETIME_SUB, "
20122048
" CASE data_type "
@@ -2048,6 +2084,8 @@ def __build_universal_all_schema_columns_query(
20482084
" WHEN 'super' THEN NULL "
20492085
" WHEN 'varbyte' THEN NULL "
20502086
" WHEN 'geography' THEN NULL "
2087+
" WHEN 'intervaly2m' THEN 32 "
2088+
" WHEN 'intervald2s' THEN 64 "
20512089
" ELSE 2147483647 "
20522090
" END AS CHAR_OCTET_LENGTH, "
20532091
" ordinal_position AS ORDINAL_POSITION, "
@@ -2127,6 +2165,8 @@ def __build_external_schema_columns_query(
21272165
" WHEN external_type = 'geometry' THEN -4"
21282166
" WHEN external_type = 'super' THEN -16"
21292167
" WHEN external_type = 'varbyte' THEN -4"
2168+
" WHEN external_type = 'intervaly2m' THEN 1111 "
2169+
" WHEN external_type = 'intervald2s' THEN 1111 "
21302170
" ELSE 1111 END AS SMALLINT) AS DATA_TYPE,"
21312171
" CASE WHEN left(external_type, 17) = 'character varying' THEN 'varchar'"
21322172
" WHEN left(external_type, 7) = 'varchar' THEN 'varchar'"
@@ -2176,6 +2216,8 @@ def __build_external_schema_columns_query(
21762216
" WHEN external_type = 'geometry' THEN NULL"
21772217
" WHEN external_type = 'super' THEN NULL"
21782218
" WHEN external_type = 'varbyte' THEN NULL"
2219+
" WHEN external_type = 'intervaly2m' THEN 32 "
2220+
" WHEN external_type = 'intervald2s' THEN 64 "
21792221
" ELSE 2147483647 END AS COLUMN_SIZE,"
21802222
" NULL AS BUFFER_LENGTH,"
21812223
" CASE WHEN external_type = 'real'THEN 8"
@@ -2190,6 +2232,8 @@ def __build_external_schema_columns_query(
21902232
" WHEN external_type = 'geometry' THEN NULL"
21912233
" WHEN external_type = 'super' THEN NULL"
21922234
" WHEN external_type = 'varbyte' THEN NULL"
2235+
" WHEN external_type = 'intervaly2m' THEN 32 "
2236+
" WHEN external_type = 'intervald2s' THEN 64 "
21932237
" ELSE 0 END AS DECIMAL_DIGITS,"
21942238
" 10 AS NUM_PREC_RADIX,"
21952239
" NULL AS NULLABLE,"
@@ -2238,6 +2282,8 @@ def __build_external_schema_columns_query(
22382282
" WHEN external_type = 'geometry' THEN -4"
22392283
" WHEN external_type = 'super' THEN -16"
22402284
" WHEN external_type = 'varbyte' THEN -4"
2285+
" WHEN external_type = 'intervaly2m' THEN 1111 "
2286+
" WHEN external_type = 'intervald2s' THEN 1111 "
22412287
" ELSE 1111 END AS SMALLINT) AS SQL_DATA_TYPE,"
22422288
" CAST(NULL AS SMALLINT) AS SQL_DATETIME_SUB,"
22432289
" CASE WHEN left(external_type, 7) = 'varchar' THEN regexp_substr(external_type, '[0-9]+', 7)::integer"

redshift_connector/interval.py

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import typing
12
from redshift_connector.config import max_int4, max_int8, min_int4, min_int8
3+
from datetime import timedelta as Timedelta
24

35

46
class Interval:
@@ -44,23 +46,23 @@ def __init__(self: "Interval", microseconds: int = 0, days: int = 0, months: int
4446
def _setMicroseconds(self: "Interval", value: int) -> None:
4547
if not isinstance(value, int):
4648
raise TypeError("microseconds must be an integer type")
47-
elif not (min_int8 < value < max_int8):
49+
elif not (min_int8 <= value < max_int8):
4850
raise OverflowError("microseconds must be representable as a 64-bit integer")
4951
else:
5052
self._microseconds = value
5153

5254
def _setDays(self: "Interval", value: int) -> None:
5355
if not isinstance(value, int):
5456
raise TypeError("days must be an integer type")
55-
elif not (min_int4 < value < max_int4):
57+
elif not (min_int4 <= value < max_int4):
5658
raise OverflowError("days must be representable as a 32-bit integer")
5759
else:
5860
self._days = value
5961

6062
def _setMonths(self: "Interval", value: int) -> None:
6163
if not isinstance(value, int):
6264
raise TypeError("months must be an integer type")
63-
elif not (min_int4 < value < max_int4):
65+
elif not (min_int4 <= value < max_int4):
6466
raise OverflowError("months must be representable as a 32-bit integer")
6567
else:
6668
self._months = value
@@ -87,3 +89,98 @@ def __neq__(self: "Interval", other: "Interval") -> bool:
8789
def total_seconds(self: "Interval") -> float:
8890
"""Total seconds in the Interval, excluding month field."""
8991
return ((self.days * 86400) * 10**6 + self.microseconds) / 10**6
92+
93+
class IntervalYearToMonth(Interval):
94+
"""An Interval Year To Month represents a measurement of time of the order
95+
of a few months and years. Note the difference with Interval which can
96+
represent any length of time. Since this class only represents an interval
97+
of the order of months, we just use the :attr:`months`, the other inherited
98+
attributes must be set to 0 at all times.
99+
100+
Note that 1year = 12months.
101+
"""
102+
def __init__(self: "IntervalYearToMonth",
103+
months: int = 0,
104+
year_month: typing.Tuple[int, int] = None) -> None:
105+
if year_month is not None:
106+
year, month = year_month
107+
self.months = year * 12 + month
108+
else:
109+
self.months = months
110+
111+
def _setMicroseconds(self: "IntervalYearToMonth", value: int) -> None:
112+
raise ValueError("microseconds cannot be set for an Interval Year To Month object")
113+
114+
def _setDays(self: "IntervalYearToMonth", value: int) -> None:
115+
raise ValueError("days cannot be set for an Interval Year To Month object")
116+
117+
def _setMonths(self: "IntervalYearToMonth", value: int) -> None:
118+
return super(IntervalYearToMonth, self)._setMonths(value)
119+
120+
# microseconds = property(lambda self: self._microseconds, _setMicroseconds)
121+
# days = property(lambda self: self._days, _setDays)
122+
months = property(lambda self: self._months, _setMonths)
123+
124+
def getYearMonth(self: "IntervalDayToSecond") -> typing.Tuple[int, int]:
125+
years = int(self.months / 12)
126+
months = self.months - 12 * years
127+
return (years, months)
128+
129+
def __repr__(self: "IntervalYearToMonth") -> str:
130+
return "<IntervalYearToMonth %s months>" % (self.months)
131+
132+
def __eq__(self: "IntervalYearToMonth", other: object) -> bool:
133+
return (
134+
other is not None
135+
and isinstance(other, IntervalYearToMonth)
136+
and self.months == other.months
137+
)
138+
139+
def __neq__(self: "IntervalYearToMonth", other: "IntervalYearToMonth") -> bool:
140+
return not self.__eq__(other)
141+
142+
class IntervalDayToSecond(Interval):
143+
"""An Interval Day To Second represents a measurement of time of the order
144+
of a few microseconds. Note the difference with Interval which can
145+
represent any length of time. Since this class only represents an interval
146+
of the order of microsecodns, we just use the :attr:`microseconds`, the other
147+
inherited attributes must be set to 0 at all times.
148+
149+
Note that 1day = 24 * 3600 * 1000000 microseconds.
150+
"""
151+
def __init__(self: "IntervalDayToSecond",
152+
microseconds: int = 0,
153+
timedelta: Timedelta = None) -> None:
154+
if timedelta is not None:
155+
self.microseconds = int(timedelta.total_seconds() * (10**6))
156+
else:
157+
self.microseconds = microseconds
158+
159+
def _setMicroseconds(self: "IntervalDayToSecond", value: int) -> None:
160+
return super(IntervalDayToSecond, self)._setMicroseconds(value)
161+
162+
def _setDays(self: "IntervalDayToSecond", value: int) -> None:
163+
raise ValueError("days cannot be set for an Interval Day To Second object")
164+
165+
def _setMonths(self: "IntervalDayToSecond", value: int) -> None:
166+
raise ValueError("months cannot be set for an Interval Day To Second object")
167+
168+
microseconds = property(lambda self: self._microseconds, _setMicroseconds)
169+
# days = property(lambda self: self._days, _setDays)
170+
# months = property(lambda self: self._months, _setMonths)
171+
172+
def __repr__(self: "IntervalDayToSecond") -> str:
173+
return "<IntervalDayToSecond %s microseconds>" % (self.microseconds)
174+
175+
def __eq__(self: "IntervalDayToSecond", other: object) -> bool:
176+
return (
177+
other is not None
178+
and isinstance(other, IntervalDayToSecond)
179+
and self.microseconds == other.microseconds
180+
)
181+
182+
def __neq__(self: "IntervalDayToSecond", other: "IntervalDayToSecond") -> bool:
183+
return not self.__eq__(other)
184+
185+
def getTimedelta(self: "IntervalDayToSecond") -> Timedelta:
186+
return Timedelta(microseconds=self.microseconds)

redshift_connector/utils/oids.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ class RedshiftOID(IntEnum):
3434
INTEGER_ARRAY = 1007
3535
INTERVAL = 1186
3636
INTERVAL_ARRAY = 1187
37+
INTERVALY2M = 1188
38+
INTERVALY2M_ARRAY = 1189
39+
INTERVALD2S = 1190
40+
INTERVALD2S_ARRAY = 1191
3741
JSON = 114
3842
JSON_ARRAY = 199
3943
JSONB = 3802

0 commit comments

Comments
 (0)