pyaccesskit 0.1.0__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.
- pyaccesskit/AGENT_GUIDE.md +455 -0
- pyaccesskit/__init__.py +167 -0
- pyaccesskit/__main__.py +6 -0
- pyaccesskit/_backends/__init__.py +0 -0
- pyaccesskit/_backends/access/__init__.py +1 -0
- pyaccesskit/_backends/access/design.py +415 -0
- pyaccesskit/_backends/dao/__init__.py +1 -0
- pyaccesskit/_backends/dao/profile.py +40 -0
- pyaccesskit/_backends/dao/schema.py +805 -0
- pyaccesskit/_backends/dao/typemap.py +390 -0
- pyaccesskit/_backends/fake/__init__.py +3 -0
- pyaccesskit/_backends/fake/backend.py +680 -0
- pyaccesskit/_backends/protocols.py +339 -0
- pyaccesskit/_com/__init__.py +1 -0
- pyaccesskit/_com/constants.py +394 -0
- pyaccesskit/_com/dispatch.py +50 -0
- pyaccesskit/_com/errors.py +184 -0
- pyaccesskit/_com/gateway.py +199 -0
- pyaccesskit/_com/raw.py +164 -0
- pyaccesskit/_com/runtime.py +39 -0
- pyaccesskit/_com/variants.py +72 -0
- pyaccesskit/_engines/__init__.py +48 -0
- pyaccesskit/_engines/access.py +300 -0
- pyaccesskit/_engines/inproc.py +148 -0
- pyaccesskit/_engines/probe.py +231 -0
- pyaccesskit/_ledger.py +158 -0
- pyaccesskit/_ops/__init__.py +0 -0
- pyaccesskit/_ops/design.py +127 -0
- pyaccesskit/_ops/schema.py +471 -0
- pyaccesskit/_session/__init__.py +1 -0
- pyaccesskit/_session/protocols.py +78 -0
- pyaccesskit/_session/session.py +354 -0
- pyaccesskit/_text/__init__.py +0 -0
- pyaccesskit/_text/codec.py +114 -0
- pyaccesskit/_version.py +3 -0
- pyaccesskit/_win/__init__.py +1 -0
- pyaccesskit/_win/access_process.py +348 -0
- pyaccesskit/_win/console.py +56 -0
- pyaccesskit/_win/inspector.py +53 -0
- pyaccesskit/_win/job.py +65 -0
- pyaccesskit/_win/processes.py +159 -0
- pyaccesskit/_win/watchdog.py +253 -0
- pyaccesskit/cli/__init__.py +10 -0
- pyaccesskit/cli/_output.py +101 -0
- pyaccesskit/cli/agent.py +99 -0
- pyaccesskit/cli/app.py +54 -0
- pyaccesskit/cli/cleanup.py +56 -0
- pyaccesskit/cli/doctor.py +101 -0
- pyaccesskit/cli/inspection.py +223 -0
- pyaccesskit/database.py +296 -0
- pyaccesskit/diagnostics.py +319 -0
- pyaccesskit/enums.py +258 -0
- pyaccesskit/errors.py +407 -0
- pyaccesskit/forms/__init__.py +45 -0
- pyaccesskit/forms/builder.py +295 -0
- pyaccesskit/forms/collection.py +117 -0
- pyaccesskit/forms/controls.py +157 -0
- pyaccesskit/forms/layout.py +300 -0
- pyaccesskit/forms/spec.py +169 -0
- pyaccesskit/forms/vba.py +138 -0
- pyaccesskit/maintenance.py +32 -0
- pyaccesskit/modules.py +101 -0
- pyaccesskit/objects.py +81 -0
- pyaccesskit/options.py +40 -0
- pyaccesskit/properties.py +74 -0
- pyaccesskit/py.typed +0 -0
- pyaccesskit/queries.py +190 -0
- pyaccesskit/relationships.py +143 -0
- pyaccesskit/schema/__init__.py +73 -0
- pyaccesskit/schema/_base.py +55 -0
- pyaccesskit/schema/_reserved_words.py +55 -0
- pyaccesskit/schema/columns.py +609 -0
- pyaccesskit/schema/compat.py +57 -0
- pyaccesskit/schema/expressions.py +162 -0
- pyaccesskit/schema/indexes.py +114 -0
- pyaccesskit/schema/names.py +122 -0
- pyaccesskit/schema/queries.py +192 -0
- pyaccesskit/schema/relationships.py +132 -0
- pyaccesskit/schema/tables.py +178 -0
- pyaccesskit/tables.py +333 -0
- pyaccesskit/units.py +301 -0
- pyaccesskit-0.1.0.dist-info/METADATA +201 -0
- pyaccesskit-0.1.0.dist-info/RECORD +86 -0
- pyaccesskit-0.1.0.dist-info/WHEEL +4 -0
- pyaccesskit-0.1.0.dist-info/entry_points.txt +2 -0
- pyaccesskit-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"""Mapping between column specs and DAO field definitions (pure Python; no COM).
|
|
2
|
+
|
|
3
|
+
The DAO adapter reads each field into a plain :class:`FieldRead` and turns specs into a :class:`FieldPlan`,
|
|
4
|
+
so the whole mapping is unit-testable. Type codes and attribute flags are the ACEDAO type-library values
|
|
5
|
+
(see ``_com/constants.py`` and ADR 0001).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from decimal import Decimal
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from pyaccesskit.enums import NumberSize, PropertyType
|
|
15
|
+
from pyaccesskit.schema import (
|
|
16
|
+
AutoNumberColumn,
|
|
17
|
+
ColumnBase,
|
|
18
|
+
CurrencyColumn,
|
|
19
|
+
DateTimeColumn,
|
|
20
|
+
DecimalColumn,
|
|
21
|
+
Expr,
|
|
22
|
+
HyperlinkColumn,
|
|
23
|
+
LongTextColumn,
|
|
24
|
+
NumberColumn,
|
|
25
|
+
OleObjectColumn,
|
|
26
|
+
TextColumn,
|
|
27
|
+
YesNoColumn,
|
|
28
|
+
parse_literal,
|
|
29
|
+
render_literal,
|
|
30
|
+
)
|
|
31
|
+
from pyaccesskit.schema.columns import UnsupportedColumn
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"DAO_PROPERTY_TYPES",
|
|
35
|
+
"FieldPlan",
|
|
36
|
+
"FieldRead",
|
|
37
|
+
"column_from_field",
|
|
38
|
+
"plan_field",
|
|
39
|
+
"property_type_for",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# DataTypeEnum
|
|
43
|
+
DB_BOOLEAN, DB_BYTE, DB_INTEGER, DB_LONG, DB_CURRENCY, DB_SINGLE, DB_DOUBLE, DB_DATE = (
|
|
44
|
+
1,
|
|
45
|
+
2,
|
|
46
|
+
3,
|
|
47
|
+
4,
|
|
48
|
+
5,
|
|
49
|
+
6,
|
|
50
|
+
7,
|
|
51
|
+
8,
|
|
52
|
+
)
|
|
53
|
+
DB_BINARY, DB_TEXT, DB_LONGBINARY, DB_MEMO, DB_GUID, DB_BIGINT, DB_DECIMAL = (
|
|
54
|
+
9,
|
|
55
|
+
10,
|
|
56
|
+
11,
|
|
57
|
+
12,
|
|
58
|
+
15,
|
|
59
|
+
16,
|
|
60
|
+
20,
|
|
61
|
+
)
|
|
62
|
+
DB_DATETIME_EXTENDED, DB_ATTACHMENT = 26, 101
|
|
63
|
+
# FieldAttributeEnum
|
|
64
|
+
DB_FIXED_FIELD, DB_VARIABLE_FIELD, DB_AUTOINCR_FIELD, DB_HYPERLINK_FIELD = 1, 2, 16, 32768
|
|
65
|
+
DB_DESCENDING = 1
|
|
66
|
+
|
|
67
|
+
REPLICATION_ID_DEFAULT = "GenGUID()"
|
|
68
|
+
AC_CHECKBOX = 106
|
|
69
|
+
DECIMAL_PLACES_AUTO = 255
|
|
70
|
+
|
|
71
|
+
_NUMBER_TYPES = {
|
|
72
|
+
NumberSize.BYTE: DB_BYTE,
|
|
73
|
+
NumberSize.INTEGER: DB_INTEGER,
|
|
74
|
+
NumberSize.LONG_INTEGER: DB_LONG,
|
|
75
|
+
NumberSize.SINGLE: DB_SINGLE,
|
|
76
|
+
NumberSize.DOUBLE: DB_DOUBLE,
|
|
77
|
+
NumberSize.REPLICATION_ID: DB_GUID,
|
|
78
|
+
}
|
|
79
|
+
_SIZE_OF = {code: size for size, code in _NUMBER_TYPES.items()}
|
|
80
|
+
|
|
81
|
+
DAO_PROPERTY_TYPES: dict[PropertyType, int] = {
|
|
82
|
+
PropertyType.BOOLEAN: DB_BOOLEAN,
|
|
83
|
+
PropertyType.BYTE: DB_BYTE,
|
|
84
|
+
PropertyType.INTEGER: DB_INTEGER,
|
|
85
|
+
PropertyType.LONG: DB_LONG,
|
|
86
|
+
PropertyType.CURRENCY: DB_CURRENCY,
|
|
87
|
+
PropertyType.SINGLE: DB_SINGLE,
|
|
88
|
+
PropertyType.DOUBLE: DB_DOUBLE,
|
|
89
|
+
PropertyType.DATE_TIME: DB_DATE,
|
|
90
|
+
PropertyType.TEXT: DB_TEXT,
|
|
91
|
+
PropertyType.MEMO: DB_MEMO,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Access-defined properties PyAccessKit writes, with the DAO type Access itself uses for them.
|
|
95
|
+
KNOWN_PROPERTY_TYPES: dict[str, int] = {
|
|
96
|
+
"description": DB_TEXT,
|
|
97
|
+
"caption": DB_TEXT,
|
|
98
|
+
"format": DB_TEXT,
|
|
99
|
+
"inputmask": DB_TEXT,
|
|
100
|
+
"decimalplaces": DB_BYTE,
|
|
101
|
+
"unicodecompression": DB_BOOLEAN,
|
|
102
|
+
"textformat": DB_BYTE,
|
|
103
|
+
"displaycontrol": DB_INTEGER,
|
|
104
|
+
"usemdimode": DB_BYTE,
|
|
105
|
+
"webdesignmode": DB_BYTE,
|
|
106
|
+
"showdocumenttabs": DB_BOOLEAN,
|
|
107
|
+
"appicon": DB_TEXT,
|
|
108
|
+
"apptitle": DB_TEXT,
|
|
109
|
+
"startupform": DB_TEXT,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def property_type_for(name: str, value: Any, explicit: PropertyType | None = None) -> int:
|
|
114
|
+
"""The DAO type to create a property with: explicit, known Access property, or inferred."""
|
|
115
|
+
if explicit is not None:
|
|
116
|
+
return DAO_PROPERTY_TYPES[explicit]
|
|
117
|
+
known = KNOWN_PROPERTY_TYPES.get(name.casefold())
|
|
118
|
+
if known is not None:
|
|
119
|
+
if known == DB_TEXT and isinstance(value, str) and len(value) > 255:
|
|
120
|
+
return DB_MEMO
|
|
121
|
+
return known
|
|
122
|
+
if isinstance(value, bool):
|
|
123
|
+
return DB_BOOLEAN
|
|
124
|
+
if isinstance(value, int):
|
|
125
|
+
return DB_LONG if -(2**31) <= value < 2**31 else DB_DOUBLE
|
|
126
|
+
if isinstance(value, float):
|
|
127
|
+
return DB_DOUBLE
|
|
128
|
+
if isinstance(value, Decimal):
|
|
129
|
+
return DB_CURRENCY
|
|
130
|
+
if isinstance(value, str) and len(value) > 255:
|
|
131
|
+
return DB_MEMO
|
|
132
|
+
if value is not None and hasattr(value, "year"):
|
|
133
|
+
return DB_DATE
|
|
134
|
+
return DB_TEXT
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclass(frozen=True)
|
|
138
|
+
class FieldPlan:
|
|
139
|
+
"""How to create one column with DAO (``ado_decimal`` columns are created with ADO DDL instead)."""
|
|
140
|
+
|
|
141
|
+
dao_type: int
|
|
142
|
+
size: int | None
|
|
143
|
+
attributes: int
|
|
144
|
+
default: str | None
|
|
145
|
+
allow_zero_length: bool | None
|
|
146
|
+
append_only: bool
|
|
147
|
+
ado_decimal: bool = False
|
|
148
|
+
post_properties: tuple[tuple[str, int, Any], ...] = ()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _post_properties(column: ColumnBase) -> list[tuple[str, int, Any]]:
|
|
152
|
+
props: list[tuple[str, int, Any]] = []
|
|
153
|
+
for name, value in (
|
|
154
|
+
("Description", column.description),
|
|
155
|
+
("Caption", column.caption),
|
|
156
|
+
("Format", column.format),
|
|
157
|
+
("InputMask", getattr(column, "input_mask", None)),
|
|
158
|
+
):
|
|
159
|
+
if value is not None:
|
|
160
|
+
props.append((name, property_type_for(name, value), value))
|
|
161
|
+
decimal_places = getattr(column, "decimal_places", None)
|
|
162
|
+
if decimal_places is not None:
|
|
163
|
+
props.append(("DecimalPlaces", DB_BYTE, decimal_places))
|
|
164
|
+
if isinstance(column, (TextColumn, LongTextColumn, HyperlinkColumn)):
|
|
165
|
+
props.append(("UnicodeCompression", DB_BOOLEAN, column.unicode_compression))
|
|
166
|
+
if isinstance(column, LongTextColumn) and column.rich_text:
|
|
167
|
+
props.append(("TextFormat", DB_BYTE, 1))
|
|
168
|
+
if isinstance(column, YesNoColumn):
|
|
169
|
+
props.append(("DisplayControl", DB_INTEGER, AC_CHECKBOX))
|
|
170
|
+
for name, value in column.properties.items():
|
|
171
|
+
props.append((name, property_type_for(name, value), value))
|
|
172
|
+
return props
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def plan_field(column: ColumnBase) -> FieldPlan:
|
|
176
|
+
"""Plan the DAO definition of ``column``.
|
|
177
|
+
|
|
178
|
+
Raises:
|
|
179
|
+
ValueError: For columns that cannot be created (introspection-only types).
|
|
180
|
+
"""
|
|
181
|
+
default = render_literal(column.default) if column.default is not None else None
|
|
182
|
+
allow_zero: bool | None = getattr(column, "allow_zero_length", None)
|
|
183
|
+
post = tuple(_post_properties(column))
|
|
184
|
+
if isinstance(column, TextColumn):
|
|
185
|
+
return FieldPlan(
|
|
186
|
+
DB_TEXT,
|
|
187
|
+
column.length,
|
|
188
|
+
DB_VARIABLE_FIELD,
|
|
189
|
+
default,
|
|
190
|
+
allow_zero,
|
|
191
|
+
False,
|
|
192
|
+
post_properties=post,
|
|
193
|
+
)
|
|
194
|
+
if isinstance(column, LongTextColumn):
|
|
195
|
+
return FieldPlan(
|
|
196
|
+
DB_MEMO,
|
|
197
|
+
None,
|
|
198
|
+
DB_VARIABLE_FIELD,
|
|
199
|
+
default,
|
|
200
|
+
allow_zero,
|
|
201
|
+
column.append_only,
|
|
202
|
+
post_properties=post,
|
|
203
|
+
)
|
|
204
|
+
if isinstance(column, HyperlinkColumn):
|
|
205
|
+
attributes = DB_VARIABLE_FIELD | DB_HYPERLINK_FIELD
|
|
206
|
+
return FieldPlan(
|
|
207
|
+
DB_MEMO, None, attributes, default, allow_zero, False, post_properties=post
|
|
208
|
+
)
|
|
209
|
+
if isinstance(column, NumberColumn):
|
|
210
|
+
return FieldPlan(
|
|
211
|
+
_NUMBER_TYPES[column.size],
|
|
212
|
+
None,
|
|
213
|
+
DB_FIXED_FIELD,
|
|
214
|
+
default,
|
|
215
|
+
None,
|
|
216
|
+
False,
|
|
217
|
+
post_properties=post,
|
|
218
|
+
)
|
|
219
|
+
if isinstance(column, DecimalColumn):
|
|
220
|
+
return FieldPlan(
|
|
221
|
+
DB_DECIMAL,
|
|
222
|
+
None,
|
|
223
|
+
DB_FIXED_FIELD,
|
|
224
|
+
default,
|
|
225
|
+
None,
|
|
226
|
+
False,
|
|
227
|
+
ado_decimal=True,
|
|
228
|
+
post_properties=post,
|
|
229
|
+
)
|
|
230
|
+
if isinstance(column, CurrencyColumn):
|
|
231
|
+
return FieldPlan(
|
|
232
|
+
DB_CURRENCY, None, DB_FIXED_FIELD, default, None, False, post_properties=post
|
|
233
|
+
)
|
|
234
|
+
if isinstance(column, AutoNumberColumn):
|
|
235
|
+
if column.replication_id:
|
|
236
|
+
return FieldPlan(
|
|
237
|
+
DB_GUID,
|
|
238
|
+
None,
|
|
239
|
+
DB_FIXED_FIELD,
|
|
240
|
+
REPLICATION_ID_DEFAULT,
|
|
241
|
+
None,
|
|
242
|
+
False,
|
|
243
|
+
post_properties=post,
|
|
244
|
+
)
|
|
245
|
+
attributes = DB_FIXED_FIELD | DB_AUTOINCR_FIELD
|
|
246
|
+
return FieldPlan(DB_LONG, None, attributes, None, None, False, post_properties=post)
|
|
247
|
+
if isinstance(column, DateTimeColumn):
|
|
248
|
+
return FieldPlan(DB_DATE, None, DB_FIXED_FIELD, default, None, False, post_properties=post)
|
|
249
|
+
if isinstance(column, YesNoColumn):
|
|
250
|
+
return FieldPlan(
|
|
251
|
+
DB_BOOLEAN, None, DB_FIXED_FIELD, default, None, False, post_properties=post
|
|
252
|
+
)
|
|
253
|
+
if isinstance(column, OleObjectColumn):
|
|
254
|
+
return FieldPlan(
|
|
255
|
+
DB_LONGBINARY, None, DB_VARIABLE_FIELD, None, None, False, post_properties=post
|
|
256
|
+
)
|
|
257
|
+
raise ValueError(
|
|
258
|
+
f"columns of type {column.data_type.value!r} can be read but not created by PyAccessKit"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@dataclass(frozen=True)
|
|
263
|
+
class FieldRead:
|
|
264
|
+
"""Plain data read from a DAO field (and its Access properties)."""
|
|
265
|
+
|
|
266
|
+
name: str
|
|
267
|
+
dao_type: int
|
|
268
|
+
size: int
|
|
269
|
+
attributes: int
|
|
270
|
+
required: bool
|
|
271
|
+
allow_zero_length: bool
|
|
272
|
+
default: str
|
|
273
|
+
validation_rule: str
|
|
274
|
+
validation_text: str
|
|
275
|
+
append_only: bool = False
|
|
276
|
+
expression: str = ""
|
|
277
|
+
properties: dict[str, Any] = field(default_factory=dict[str, Any])
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _prop(read: FieldRead, name: str) -> Any:
|
|
281
|
+
return read.properties.get(name.casefold())
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _text(value: Any) -> str | None:
|
|
285
|
+
return value if isinstance(value, str) and value != "" else None
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def column_from_field(read: FieldRead) -> ColumnBase:
|
|
289
|
+
"""Rebuild the canonical column spec of a DAO field."""
|
|
290
|
+
common: dict[str, Any] = {
|
|
291
|
+
"name": read.name,
|
|
292
|
+
"description": _text(_prop(read, "Description")),
|
|
293
|
+
"caption": _text(_prop(read, "Caption")),
|
|
294
|
+
"format": _text(_prop(read, "Format")),
|
|
295
|
+
"validation_rule": _text(read.validation_rule),
|
|
296
|
+
"validation_text": _text(read.validation_text) if _text(read.validation_rule) else None,
|
|
297
|
+
}
|
|
298
|
+
default_text = read.default.strip() if read.default else ""
|
|
299
|
+
default: Any = parse_literal(default_text) if default_text else None
|
|
300
|
+
input_mask = _text(_prop(read, "InputMask"))
|
|
301
|
+
places = _prop(read, "DecimalPlaces")
|
|
302
|
+
decimal_places = None if places is None or int(places) == DECIMAL_PLACES_AUTO else int(places)
|
|
303
|
+
unicode_value = _prop(read, "UnicodeCompression")
|
|
304
|
+
unicode_compression = True if unicode_value is None else bool(unicode_value)
|
|
305
|
+
auto = bool(read.attributes & DB_AUTOINCR_FIELD)
|
|
306
|
+
|
|
307
|
+
if read.expression:
|
|
308
|
+
return UnsupportedColumn(**common, dao_type=read.dao_type, detail="calculated field")
|
|
309
|
+
code = read.dao_type
|
|
310
|
+
if code == DB_TEXT:
|
|
311
|
+
return TextColumn(
|
|
312
|
+
**common,
|
|
313
|
+
required=read.required,
|
|
314
|
+
default=default,
|
|
315
|
+
length=read.size,
|
|
316
|
+
allow_zero_length=read.allow_zero_length,
|
|
317
|
+
unicode_compression=unicode_compression,
|
|
318
|
+
input_mask=input_mask,
|
|
319
|
+
)
|
|
320
|
+
if code == DB_MEMO:
|
|
321
|
+
if read.attributes & DB_HYPERLINK_FIELD:
|
|
322
|
+
return HyperlinkColumn(
|
|
323
|
+
**common,
|
|
324
|
+
required=read.required,
|
|
325
|
+
default=default,
|
|
326
|
+
allow_zero_length=read.allow_zero_length,
|
|
327
|
+
unicode_compression=unicode_compression,
|
|
328
|
+
)
|
|
329
|
+
text_format = _prop(read, "TextFormat")
|
|
330
|
+
return LongTextColumn(
|
|
331
|
+
**common,
|
|
332
|
+
required=read.required,
|
|
333
|
+
default=default,
|
|
334
|
+
rich_text=bool(text_format) and int(text_format) == 1,
|
|
335
|
+
append_only=read.append_only,
|
|
336
|
+
allow_zero_length=read.allow_zero_length,
|
|
337
|
+
unicode_compression=unicode_compression,
|
|
338
|
+
)
|
|
339
|
+
if code == DB_LONG and auto:
|
|
340
|
+
return AutoNumberColumn(**common)
|
|
341
|
+
if code == DB_GUID and default_text.casefold() == REPLICATION_ID_DEFAULT.casefold():
|
|
342
|
+
return AutoNumberColumn(**common, replication_id=True)
|
|
343
|
+
if code in _SIZE_OF:
|
|
344
|
+
size = _SIZE_OF[code]
|
|
345
|
+
return NumberColumn(
|
|
346
|
+
**common,
|
|
347
|
+
required=read.required,
|
|
348
|
+
default=default
|
|
349
|
+
if size is not NumberSize.REPLICATION_ID or isinstance(default, Expr)
|
|
350
|
+
else None,
|
|
351
|
+
size=size,
|
|
352
|
+
decimal_places=decimal_places,
|
|
353
|
+
input_mask=input_mask,
|
|
354
|
+
)
|
|
355
|
+
if code == DB_DECIMAL:
|
|
356
|
+
precision = _prop(read, "Precision")
|
|
357
|
+
scale = _prop(read, "Scale")
|
|
358
|
+
return DecimalColumn(
|
|
359
|
+
**common,
|
|
360
|
+
required=read.required,
|
|
361
|
+
default=default,
|
|
362
|
+
precision=int(precision) if precision else 18,
|
|
363
|
+
scale=int(scale) if scale is not None else 0,
|
|
364
|
+
decimal_places=decimal_places,
|
|
365
|
+
input_mask=input_mask,
|
|
366
|
+
)
|
|
367
|
+
if code == DB_CURRENCY:
|
|
368
|
+
return CurrencyColumn(
|
|
369
|
+
**common,
|
|
370
|
+
required=read.required,
|
|
371
|
+
default=default,
|
|
372
|
+
decimal_places=decimal_places,
|
|
373
|
+
input_mask=input_mask,
|
|
374
|
+
)
|
|
375
|
+
if code == DB_DATE:
|
|
376
|
+
return DateTimeColumn(
|
|
377
|
+
**common, required=read.required, default=default, input_mask=input_mask
|
|
378
|
+
)
|
|
379
|
+
if code == DB_BOOLEAN:
|
|
380
|
+
return YesNoColumn(**common, required=read.required, default=default)
|
|
381
|
+
if code == DB_LONGBINARY:
|
|
382
|
+
return OleObjectColumn(**common, required=read.required)
|
|
383
|
+
details = {
|
|
384
|
+
DB_BIGINT: "Large Number",
|
|
385
|
+
DB_DATETIME_EXTENDED: "Date/Time Extended",
|
|
386
|
+
DB_ATTACHMENT: "Attachment",
|
|
387
|
+
DB_BINARY: "Binary",
|
|
388
|
+
}
|
|
389
|
+
detail = details.get(code, "multi-valued field" if 102 <= code <= 109 else f"DAO type {code}")
|
|
390
|
+
return UnsupportedColumn(**common, dao_type=code, detail=detail)
|