DefPixel-PostgreSQL 0.1.5__83-py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- DefPixel/DataAccessObject/Implements/PostgreSQL/Adapters/Array.py +16 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Adapters/JavaScriptObjectNotation.py +74 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Adapters/__init__.py +51 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Column.py +34 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Compiler.py +161 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Configuration.py +116 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Connection.py +138 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Constraint.py +18 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Constraints/Check.py +23 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Constraints/Columns.py +24 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Constraints/ForeignKey.py +56 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Constraints/PrimaryKey.py +21 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Constraints/Unique.py +25 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Constraints/__init__.py +13 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Context.py +48 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Cursor.py +123 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Executors/Create.py +181 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Executors/Delete.py +31 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Executors/Drop.py +53 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Executors/Get.py +24 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Executors/Insert.py +98 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Executors/Query.py +37 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Executors/Select.py +122 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Executors/Update.py +57 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Executors/__init__.py +19 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Expr.py +11 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Exprs/Common.py +180 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Exprs/Compare.py +87 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Exprs/Condition.py +155 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Exprs/JavaScriptObjectNotation.py +53 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Exprs/Operator.py +42 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Exprs/String.py +48 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Exprs/__init__.py +75 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Function.py +11 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Functions/Common.py +142 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Functions/DateTime.py +219 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Functions/JavaScriptObjectNotation.py +100 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Functions/Sequence.py +55 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Functions/__init__.py +91 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Index.py +135 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Joins/FullOuter.py +15 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Joins/Inner.py +15 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Joins/Join.py +41 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Joins/LeftOuter.py +15 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Joins/RightOuter.py +15 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Joins/__init__.py +15 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Mapping.py +359 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Orders.py +59 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Pool.py +84 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Schema.py +28 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Sequence.py +55 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Session.py +84 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Type.py +113 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Types/Array.py +61 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Types/Binary.py +34 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Types/Bool.py +29 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Types/DateTime.py +120 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Types/JavaScriptObjectNotation.py +52 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Types/Numeric.py +151 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Types/Size.py +13 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Types/String.py +80 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/Types/__init__.py +76 -0
- DefPixel/DataAccessObject/Implements/PostgreSQL/__init__.py +85 -0
- defpixel_postgresql-0.1.5.dist-info/METADATA +180 -0
- defpixel_postgresql-0.1.5.dist-info/RECORD +68 -0
- defpixel_postgresql-0.1.5.dist-info/WHEEL +6 -0
- defpixel_postgresql-0.1.5.dist-info/licenses/LICENSE +674 -0
- defpixel_postgresql-0.1.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from dataclasses import MISSING
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
Callable,
|
|
7
|
+
Optional,
|
|
8
|
+
Tuple,
|
|
9
|
+
Type as PythonType,
|
|
10
|
+
Union,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .Compiler import (
|
|
14
|
+
Compilable,
|
|
15
|
+
Compiler,
|
|
16
|
+
identifier,
|
|
17
|
+
qualified,
|
|
18
|
+
valuetype,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = (
|
|
22
|
+
'Type',
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Type(Compilable):
|
|
27
|
+
"""PostgreSQL column specification for SQL types and value conversion."""
|
|
28
|
+
|
|
29
|
+
typestr = ''
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
key: Optional[str],
|
|
34
|
+
type: Union[PythonType, Tuple[PythonType, ...]],
|
|
35
|
+
typestr: Optional[str] = None,
|
|
36
|
+
va: Optional[Callable] = None,
|
|
37
|
+
null: bool = False,
|
|
38
|
+
default: Any = MISSING,
|
|
39
|
+
):
|
|
40
|
+
if not isinstance(null, bool):
|
|
41
|
+
raise TypeError('null must be a bool')
|
|
42
|
+
self.key = key
|
|
43
|
+
self.type = type
|
|
44
|
+
if typestr is not None:
|
|
45
|
+
self.typestr = typestr
|
|
46
|
+
self.validator = va
|
|
47
|
+
self.null = null
|
|
48
|
+
self.coldef = default
|
|
49
|
+
self.model: Optional[str] = None
|
|
50
|
+
self.schema: Optional[str] = None
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
def __init_subclass__(
|
|
54
|
+
cls,
|
|
55
|
+
typestr: Optional[str] = None,
|
|
56
|
+
**kwargs,
|
|
57
|
+
):
|
|
58
|
+
super().__init_subclass__(**kwargs)
|
|
59
|
+
if typestr is not None:
|
|
60
|
+
cls.typestr = typestr
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
def compile(self, compiler: Compiler) -> str:
|
|
64
|
+
if self.model is None or self.key is None:
|
|
65
|
+
raise ValueError('The column is not bound to a model')
|
|
66
|
+
if not compiler.qualify:
|
|
67
|
+
return identifier(self.key)
|
|
68
|
+
return '{}.{}'.format(qualified(self.model, self.schema), identifier(self.key))
|
|
69
|
+
|
|
70
|
+
def convert(self, value: Any) -> Any:
|
|
71
|
+
"""Convert an accepted input representation before the type check."""
|
|
72
|
+
return value
|
|
73
|
+
|
|
74
|
+
def check(self, value: Any) -> bool:
|
|
75
|
+
if isinstance(self.type, tuple):
|
|
76
|
+
types = self.type
|
|
77
|
+
else:
|
|
78
|
+
types = (self.type,)
|
|
79
|
+
if bool not in types and isinstance(value, bool):
|
|
80
|
+
return False
|
|
81
|
+
return isinstance(value, types)
|
|
82
|
+
|
|
83
|
+
def validate(self, value: Any) -> Any:
|
|
84
|
+
if value is None:
|
|
85
|
+
if not self.null:
|
|
86
|
+
raise ValueError('Column {} does not allow None'.format(self.key))
|
|
87
|
+
return None
|
|
88
|
+
if self.validator is not None:
|
|
89
|
+
value = self.validator(value)
|
|
90
|
+
value = self.convert(value)
|
|
91
|
+
if not self.check(value):
|
|
92
|
+
raise TypeError('Column {} requires a value of type {}'.format(
|
|
93
|
+
self.key,
|
|
94
|
+
' or '.join(t.__name__ for t in (self.type if isinstance(self.type, tuple) else (self.type,))),
|
|
95
|
+
))
|
|
96
|
+
return value
|
|
97
|
+
|
|
98
|
+
def adapt(self, value: Any) -> Any:
|
|
99
|
+
"""Wrap a validated value for the driver."""
|
|
100
|
+
return value
|
|
101
|
+
|
|
102
|
+
def encode(self, value: Any) -> Any:
|
|
103
|
+
value = self.validate(value)
|
|
104
|
+
return None if value is None else self.adapt(value)
|
|
105
|
+
|
|
106
|
+
def default(self) -> Optional[str]:
|
|
107
|
+
"""Render the column DEFAULT clause body in DDL literal mode."""
|
|
108
|
+
if self.coldef is MISSING:
|
|
109
|
+
return None
|
|
110
|
+
compiler = Compiler(literal=True)
|
|
111
|
+
if isinstance(self.coldef, Compilable):
|
|
112
|
+
return compiler.compile(self.coldef)
|
|
113
|
+
return compiler.bind(self.encode(self.coldef), cast=valuetype(self))
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from dataclasses import MISSING
|
|
5
|
+
|
|
6
|
+
from ..Compiler import typename
|
|
7
|
+
from ..Type import Type
|
|
8
|
+
from .Size import positive
|
|
9
|
+
|
|
10
|
+
__all__ = (
|
|
11
|
+
'Array',
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Array(Type, typestr='ARRAY'):
|
|
16
|
+
"""Array column of an element type; size sets declared dimensions, for example [3] or [2, 2]."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
name=None,
|
|
21
|
+
type=None,
|
|
22
|
+
typesize=None,
|
|
23
|
+
size=None,
|
|
24
|
+
va=None,
|
|
25
|
+
null=False,
|
|
26
|
+
default=MISSING,
|
|
27
|
+
):
|
|
28
|
+
if type is None:
|
|
29
|
+
raise ValueError('Array requires an element type')
|
|
30
|
+
element = typename(type, escape=False)
|
|
31
|
+
typesize = positive('typesize', typesize)
|
|
32
|
+
if size is None:
|
|
33
|
+
sizes = [None]
|
|
34
|
+
elif isinstance(size, Sequence) and not isinstance(size, str):
|
|
35
|
+
sizes = list(size)
|
|
36
|
+
if not sizes:
|
|
37
|
+
raise ValueError('size requires at least one dimension')
|
|
38
|
+
else:
|
|
39
|
+
sizes = [size]
|
|
40
|
+
dimensions = ''.join(
|
|
41
|
+
'[{}]'.format('' if value is None else positive('size', value))
|
|
42
|
+
for value in sizes
|
|
43
|
+
)
|
|
44
|
+
super().__init__(
|
|
45
|
+
key=name,
|
|
46
|
+
type=list,
|
|
47
|
+
typestr='{}{}{}'.format(
|
|
48
|
+
element,
|
|
49
|
+
'({})'.format(typesize) if typesize else '',
|
|
50
|
+
dimensions,
|
|
51
|
+
),
|
|
52
|
+
va=va,
|
|
53
|
+
null=null,
|
|
54
|
+
default=default,
|
|
55
|
+
)
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
def convert(self, value):
|
|
59
|
+
if isinstance(value, tuple):
|
|
60
|
+
return list(value)
|
|
61
|
+
return value
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from dataclasses import MISSING
|
|
4
|
+
|
|
5
|
+
from ..Type import Type
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
'Binary',
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Binary(Type, typestr='BYTEA'):
|
|
13
|
+
"""BYTEA column accepting bytes, bytearray, or memoryview values as bytes."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
name=None,
|
|
18
|
+
va=None,
|
|
19
|
+
null=False,
|
|
20
|
+
default=MISSING,
|
|
21
|
+
):
|
|
22
|
+
super().__init__(
|
|
23
|
+
key=name,
|
|
24
|
+
type=bytes,
|
|
25
|
+
va=va,
|
|
26
|
+
null=null,
|
|
27
|
+
default=default,
|
|
28
|
+
)
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
def convert(self, value):
|
|
32
|
+
if isinstance(value, (bytearray, memoryview)):
|
|
33
|
+
return bytes(value)
|
|
34
|
+
return value
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from dataclasses import MISSING
|
|
4
|
+
|
|
5
|
+
from ..Type import Type
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
'Bool',
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Bool(Type, typestr='BOOLEAN'):
|
|
13
|
+
"""BOOLEAN column specification."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
name=None,
|
|
18
|
+
va=None,
|
|
19
|
+
null=False,
|
|
20
|
+
default=MISSING,
|
|
21
|
+
):
|
|
22
|
+
super().__init__(
|
|
23
|
+
key=name,
|
|
24
|
+
type=bool,
|
|
25
|
+
va=va,
|
|
26
|
+
null=null,
|
|
27
|
+
default=default,
|
|
28
|
+
)
|
|
29
|
+
return
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from dataclasses import MISSING
|
|
4
|
+
from datetime import (
|
|
5
|
+
date,
|
|
6
|
+
datetime,
|
|
7
|
+
time,
|
|
8
|
+
timezone as tz,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from ..Type import Type
|
|
12
|
+
|
|
13
|
+
__all__ = (
|
|
14
|
+
'Timestamp',
|
|
15
|
+
'Date',
|
|
16
|
+
'Time',
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Timestamp(Type, typestr='TIMESTAMP'):
|
|
21
|
+
"""TIMESTAMP column; timezone=True requires aware values, otherwise values must be naive."""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
name=None,
|
|
26
|
+
timezone=False,
|
|
27
|
+
va=None,
|
|
28
|
+
null=False,
|
|
29
|
+
default=MISSING,
|
|
30
|
+
):
|
|
31
|
+
if not isinstance(timezone, bool):
|
|
32
|
+
raise TypeError('timezone must be a bool')
|
|
33
|
+
super().__init__(
|
|
34
|
+
key=name,
|
|
35
|
+
type=datetime,
|
|
36
|
+
typestr='TIMESTAMP WITH TIME ZONE' if timezone else 'TIMESTAMP',
|
|
37
|
+
va=va,
|
|
38
|
+
null=null,
|
|
39
|
+
default=default,
|
|
40
|
+
)
|
|
41
|
+
self.timezone = timezone
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
def convert(self, value):
|
|
45
|
+
if isinstance(value, str):
|
|
46
|
+
return datetime.fromisoformat(value)
|
|
47
|
+
if isinstance(value, float):
|
|
48
|
+
return datetime.fromtimestamp(value, tz=tz.utc if self.timezone else None)
|
|
49
|
+
return value
|
|
50
|
+
|
|
51
|
+
def check(self, value) -> bool:
|
|
52
|
+
if not super().check(value):
|
|
53
|
+
return False
|
|
54
|
+
aware = value.tzinfo is not None and value.tzinfo.utcoffset(value) is not None
|
|
55
|
+
if aware != self.timezone:
|
|
56
|
+
raise ValueError('Column {} requires a timezone-{} datetime'.format(
|
|
57
|
+
self.key,
|
|
58
|
+
'aware' if self.timezone else 'naive',
|
|
59
|
+
))
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Date(Type, typestr='DATE'):
|
|
64
|
+
"""DATE column accepting date objects or ISO strings; datetime values are rejected."""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
name=None,
|
|
69
|
+
va=None,
|
|
70
|
+
null=False,
|
|
71
|
+
default=MISSING,
|
|
72
|
+
):
|
|
73
|
+
super().__init__(
|
|
74
|
+
key=name,
|
|
75
|
+
type=date,
|
|
76
|
+
va=va,
|
|
77
|
+
null=null,
|
|
78
|
+
default=default,
|
|
79
|
+
)
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
def convert(self, value):
|
|
83
|
+
if isinstance(value, str):
|
|
84
|
+
return date.fromisoformat(value)
|
|
85
|
+
return value
|
|
86
|
+
|
|
87
|
+
def check(self, value) -> bool:
|
|
88
|
+
return super().check(value) and not isinstance(value, datetime)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Time(Type, typestr='TIME'):
|
|
92
|
+
"""TIME column accepting only timezone-naive time objects or ISO strings."""
|
|
93
|
+
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
name=None,
|
|
97
|
+
va=None,
|
|
98
|
+
null=False,
|
|
99
|
+
default=MISSING,
|
|
100
|
+
):
|
|
101
|
+
super().__init__(
|
|
102
|
+
key=name,
|
|
103
|
+
type=time,
|
|
104
|
+
va=va,
|
|
105
|
+
null=null,
|
|
106
|
+
default=default,
|
|
107
|
+
)
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
def convert(self, value):
|
|
111
|
+
if isinstance(value, str):
|
|
112
|
+
return time.fromisoformat(value)
|
|
113
|
+
return value
|
|
114
|
+
|
|
115
|
+
def check(self, value) -> bool:
|
|
116
|
+
if not super().check(value):
|
|
117
|
+
return False
|
|
118
|
+
if value.tzinfo is not None and value.utcoffset() is not None:
|
|
119
|
+
raise ValueError('Column {} requires a timezone-naive time'.format(self.key))
|
|
120
|
+
return True
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from dataclasses import MISSING
|
|
4
|
+
|
|
5
|
+
from psycopg.types.json import (
|
|
6
|
+
Json,
|
|
7
|
+
Jsonb,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from ..Adapters.JavaScriptObjectNotation import encode
|
|
11
|
+
from ..Type import Type
|
|
12
|
+
|
|
13
|
+
__all__ = (
|
|
14
|
+
'JavaScriptObjectNotation',
|
|
15
|
+
'JavaScriptObjectNotationByteArray',
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def dumps(value) -> str:
|
|
20
|
+
from json import dumps as serialize
|
|
21
|
+
return serialize(value, default=encode, allow_nan=False, ensure_ascii=False)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class JavaScriptObjectNotation(Type, typestr='JSON'):
|
|
25
|
+
"""JSON column accepting dict or list values."""
|
|
26
|
+
|
|
27
|
+
wrapper = Json
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
name=None,
|
|
32
|
+
va=None,
|
|
33
|
+
null=False,
|
|
34
|
+
default=MISSING,
|
|
35
|
+
):
|
|
36
|
+
super().__init__(
|
|
37
|
+
key=name,
|
|
38
|
+
type=(dict, list),
|
|
39
|
+
va=va,
|
|
40
|
+
null=null,
|
|
41
|
+
default=default,
|
|
42
|
+
)
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
def adapt(self, value):
|
|
46
|
+
return self.wrapper(value, dumps=dumps)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class JavaScriptObjectNotationByteArray(JavaScriptObjectNotation, typestr='JSONB'):
|
|
50
|
+
"""JSONB column accepting dict or list values."""
|
|
51
|
+
|
|
52
|
+
wrapper = Jsonb
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from dataclasses import MISSING
|
|
4
|
+
from decimal import Decimal as PythonDecimal
|
|
5
|
+
|
|
6
|
+
from ..Type import Type
|
|
7
|
+
from .Size import positive
|
|
8
|
+
|
|
9
|
+
__all__ = (
|
|
10
|
+
'Short',
|
|
11
|
+
'Integer',
|
|
12
|
+
'Long',
|
|
13
|
+
'Float',
|
|
14
|
+
'Double',
|
|
15
|
+
'Decimal',
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Short(Type, typestr='SMALLINT'):
|
|
20
|
+
"""SMALLINT column specification."""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
name=None,
|
|
25
|
+
va=None,
|
|
26
|
+
null=False,
|
|
27
|
+
default=MISSING,
|
|
28
|
+
):
|
|
29
|
+
super().__init__(
|
|
30
|
+
key=name,
|
|
31
|
+
type=int,
|
|
32
|
+
va=va,
|
|
33
|
+
null=null,
|
|
34
|
+
default=default,
|
|
35
|
+
)
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Integer(Type, typestr='INTEGER'):
|
|
40
|
+
"""INTEGER column specification."""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
name=None,
|
|
45
|
+
va=None,
|
|
46
|
+
null=False,
|
|
47
|
+
default=MISSING,
|
|
48
|
+
):
|
|
49
|
+
super().__init__(
|
|
50
|
+
key=name,
|
|
51
|
+
type=int,
|
|
52
|
+
va=va,
|
|
53
|
+
null=null,
|
|
54
|
+
default=default,
|
|
55
|
+
)
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Long(Type, typestr='BIGINT'):
|
|
60
|
+
"""BIGINT column specification."""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
name=None,
|
|
65
|
+
va=None,
|
|
66
|
+
null=False,
|
|
67
|
+
default=MISSING,
|
|
68
|
+
):
|
|
69
|
+
super().__init__(
|
|
70
|
+
key=name,
|
|
71
|
+
type=int,
|
|
72
|
+
va=va,
|
|
73
|
+
null=null,
|
|
74
|
+
default=default,
|
|
75
|
+
)
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class FloatingPoint(Type):
|
|
80
|
+
"""Floating-point column accepting int values as floats."""
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
name=None,
|
|
85
|
+
va=None,
|
|
86
|
+
null=False,
|
|
87
|
+
default=MISSING,
|
|
88
|
+
):
|
|
89
|
+
super().__init__(
|
|
90
|
+
key=name,
|
|
91
|
+
type=float,
|
|
92
|
+
va=va,
|
|
93
|
+
null=null,
|
|
94
|
+
default=default,
|
|
95
|
+
)
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
def convert(self, value):
|
|
99
|
+
if isinstance(value, int) and not isinstance(value, bool):
|
|
100
|
+
return float(value)
|
|
101
|
+
return value
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class Float(FloatingPoint, typestr='REAL'):
|
|
105
|
+
"""REAL column specification."""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class Double(FloatingPoint, typestr='DOUBLE PRECISION'):
|
|
109
|
+
"""DOUBLE PRECISION column specification."""
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class Decimal(Type, typestr='NUMERIC'):
|
|
113
|
+
"""NUMERIC(precision, scale) column specification accepting int values as Decimal."""
|
|
114
|
+
|
|
115
|
+
def __init__(
|
|
116
|
+
self,
|
|
117
|
+
name=None,
|
|
118
|
+
precision=None,
|
|
119
|
+
scale=None,
|
|
120
|
+
va=None,
|
|
121
|
+
null=False,
|
|
122
|
+
default=MISSING,
|
|
123
|
+
):
|
|
124
|
+
precision = positive('precision', precision)
|
|
125
|
+
if scale is not None:
|
|
126
|
+
if precision is None:
|
|
127
|
+
raise ValueError('scale requires precision')
|
|
128
|
+
if type(scale) is not int:
|
|
129
|
+
raise ValueError('scale must be an int')
|
|
130
|
+
if precision is None:
|
|
131
|
+
typestr = 'NUMERIC'
|
|
132
|
+
elif scale is None:
|
|
133
|
+
typestr = 'NUMERIC({})'.format(precision)
|
|
134
|
+
else:
|
|
135
|
+
typestr = 'NUMERIC({},{})'.format(precision, scale)
|
|
136
|
+
super().__init__(
|
|
137
|
+
key=name,
|
|
138
|
+
type=PythonDecimal,
|
|
139
|
+
typestr=typestr,
|
|
140
|
+
va=va,
|
|
141
|
+
null=null,
|
|
142
|
+
default=default,
|
|
143
|
+
)
|
|
144
|
+
self.precision = precision
|
|
145
|
+
self.scale = scale
|
|
146
|
+
return
|
|
147
|
+
|
|
148
|
+
def convert(self, value):
|
|
149
|
+
if isinstance(value, int) and not isinstance(value, bool):
|
|
150
|
+
return PythonDecimal(value)
|
|
151
|
+
return value
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
__all__ = (
|
|
4
|
+
'positive',
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def positive(name: str, value, null: bool = True):
|
|
9
|
+
if value is None and null:
|
|
10
|
+
return None
|
|
11
|
+
if type(value) is not int or value < 1:
|
|
12
|
+
raise ValueError('{} must be a positive int'.format(name))
|
|
13
|
+
return value
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from dataclasses import MISSING
|
|
4
|
+
|
|
5
|
+
from ..Type import Type
|
|
6
|
+
from .Size import positive
|
|
7
|
+
|
|
8
|
+
__all__ = (
|
|
9
|
+
'Char',
|
|
10
|
+
'String',
|
|
11
|
+
'Text',
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Char(Type, typestr='CHAR'):
|
|
16
|
+
"""CHAR(size) column specification; PostgreSQL pads stored values with spaces."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
name=None,
|
|
21
|
+
size=None,
|
|
22
|
+
va=None,
|
|
23
|
+
null=False,
|
|
24
|
+
default=MISSING,
|
|
25
|
+
):
|
|
26
|
+
size = positive('size', size)
|
|
27
|
+
super().__init__(
|
|
28
|
+
key=name,
|
|
29
|
+
type=str,
|
|
30
|
+
typestr='CHAR({})'.format(size) if size else 'CHAR',
|
|
31
|
+
va=va,
|
|
32
|
+
null=null,
|
|
33
|
+
default=default,
|
|
34
|
+
)
|
|
35
|
+
self.size = size
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class String(Type, typestr='VARCHAR'):
|
|
40
|
+
"""VARCHAR(size) column specification."""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
name=None,
|
|
45
|
+
size=None,
|
|
46
|
+
va=None,
|
|
47
|
+
null=False,
|
|
48
|
+
default=MISSING,
|
|
49
|
+
):
|
|
50
|
+
size = positive('size', size)
|
|
51
|
+
super().__init__(
|
|
52
|
+
key=name,
|
|
53
|
+
type=str,
|
|
54
|
+
typestr='VARCHAR({})'.format(size) if size else 'VARCHAR',
|
|
55
|
+
va=va,
|
|
56
|
+
null=null,
|
|
57
|
+
default=default,
|
|
58
|
+
)
|
|
59
|
+
self.size = size
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Text(Type, typestr='TEXT'):
|
|
64
|
+
"""TEXT column specification."""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
name=None,
|
|
69
|
+
va=None,
|
|
70
|
+
null=False,
|
|
71
|
+
default=MISSING,
|
|
72
|
+
):
|
|
73
|
+
super().__init__(
|
|
74
|
+
key=name,
|
|
75
|
+
type=str,
|
|
76
|
+
va=va,
|
|
77
|
+
null=null,
|
|
78
|
+
default=default,
|
|
79
|
+
)
|
|
80
|
+
return
|