Skip to content

Commit 4a019aa

Browse files
committed
more ARRAY
1 parent cf9c0c9 commit 4a019aa

File tree

6 files changed

+107
-90
lines changed

6 files changed

+107
-90
lines changed

stix2/datastore/relational_db/database_backends/postgres_backend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class PostgresBackend(DatabaseBackend):
3131
f"{os.getenv('POSTGRES_PORT', '5432')}/postgres"
3232

3333
def __init__(self, database_connection_url=default_database_connection_url, force_recreate=False, **kwargs: Any):
34-
super().__init__(database_connection_url, force_recreate=False, **kwargs)
34+
super().__init__(database_connection_url, force_recreate=force_recreate, **kwargs)
3535

3636
def _create_schemas(self):
3737
with self.database_connection.begin() as trans:

stix2/datastore/relational_db/input_creation.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,10 @@ def generate_insert_information(self, name, stix_object, **kwargs): # noqa: F81
184184

185185
@add_method(ListProperty)
186186
def generate_insert_information( # noqa: F811
187-
self, name, stix_object, level=0, is_extension=False,
187+
self, name, stix_object, data_sink=None, level=0, is_extension=False,
188188
foreign_key_value=None, schema_name=None, **kwargs,
189189
):
190-
data_sink = kwargs.get("data_sink")
190+
db_backend = data_sink.db_backend
191191
table_name = kwargs.get("table_name")
192192
if isinstance(self.contained, ReferenceProperty):
193193
insert_statements = list()
@@ -226,7 +226,7 @@ def generate_insert_information( # noqa: F811
226226
elif isinstance(self.contained, EmbeddedObjectProperty):
227227
insert_statements = list()
228228
for value in stix_object[name]:
229-
with data_sink.database_connection.begin() as trans:
229+
with db_backend.database_connection.begin() as trans:
230230
next_id = trans.execute(data_sink.sequence)
231231
table = data_sink.tables_dictionary[canonicalize_table_name(table_name + "_" + name, schema_name)]
232232
bindings = {
@@ -248,7 +248,22 @@ def generate_insert_information( # noqa: F811
248248
)
249249
return insert_statements
250250
else:
251-
return {name: stix_object[name]}
251+
if db_backend.array_allowed():
252+
return {name: stix_object[name]}
253+
else:
254+
insert_statements = list()
255+
table = data_sink.tables_dictionary[
256+
canonicalize_table_name(
257+
table_name + "_" + name,
258+
schema_name,
259+
)
260+
]
261+
for elem in stix_object[name]:
262+
bindings = {"id": stix_object["id"], name: elem}
263+
insert_statements.append(insert(table).values(bindings))
264+
return insert_statements
265+
266+
252267

253268

254269
@add_method(ReferenceProperty)
@@ -286,6 +301,7 @@ def generate_insert_for_array_in_table(table, values, foreign_key_value):
286301

287302

288303
def generate_insert_for_external_references(data_sink, stix_object):
304+
db_backend = data_sink.db_backend
289305
insert_statements = list()
290306
next_id = None
291307
object_table = data_sink.tables_dictionary["common.external_references"]
@@ -295,7 +311,7 @@ def generate_insert_for_external_references(data_sink, stix_object):
295311
if prop in er:
296312
bindings[prop] = er[prop]
297313
if "hashes" in er:
298-
with data_sink.database_connection.begin() as trans:
314+
with db_backend.database_connection.begin() as trans:
299315
next_id = trans.execute(data_sink.sequence)
300316
bindings["hash_ref_id"] = next_id
301317
else:
@@ -326,17 +342,22 @@ def generate_insert_for_granular_markings(data_sink, granular_markings_table, st
326342
bindings["marking_ref"] = marking_ref_value
327343
if db_backend.array_allowed():
328344
bindings["selectors"] = granular_marking.get("selectors")
345+
insert_statements.append(insert(granular_markings_table).values(bindings))
329346
else:
347+
with db_backend.database_connection.begin() as trans:
348+
next_id = trans.execute(data_sink.sequence)
349+
bindings["selectors"] = next_id
350+
insert_statements.append(insert(granular_markings_table).values(bindings))
330351
table = data_sink.tables_dictionary[
331352
canonicalize_table_name(
332-
granular_markings_table + "_selector",
353+
granular_markings_table.name + "_selector",
333354
db_backend.schema_for_core(),
334355
)
335356
]
336357
for sel in granular_marking.get("selectors"):
337-
selector_bindings = {"id": stix_object["id"], "selector": sel}
358+
selector_bindings = {"id": next_id, "selector": sel}
338359
insert_statements.append(insert(table).values(selector_bindings))
339-
insert_statements.append(insert(granular_markings_table).values(bindings))
360+
340361
return insert_statements
341362

342363

stix2/datastore/relational_db/relational_db.py

Lines changed: 10 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def _add(store, stix_data, allow_custom=True, version="2.1"):
5757
class RelationalDBStore(DataStoreMixin):
5858
def __init__(
5959
self, db_backend, allow_custom=True, version=None,
60-
instantiate_database=True, *stix_object_classes,
60+
instantiate_database=True, print_sql=False, *stix_object_classes,
6161
):
6262
"""
6363
Initialize this store.
@@ -91,6 +91,7 @@ def __init__(
9191
),
9292
sink=RelationalDBSink(
9393
db_backend,
94+
print_sql=print_sql,
9495
allow_custom=allow_custom,
9596
version=version,
9697
instantiate_database=instantiate_database,
@@ -102,7 +103,7 @@ def __init__(
102103
class RelationalDBSink(DataSink):
103104
def __init__(
104105
self, db_backend, allow_custom=True, version=None,
105-
instantiate_database=True, *stix_object_classes, metadata=None,
106+
instantiate_database=True, print_sql=False, *stix_object_classes, metadata=None,
106107
):
107108
"""
108109
Initialize this sink. Only one of stix_object_classes and metadata
@@ -134,15 +135,6 @@ def __init__(
134135

135136
self.db_backend = db_backend
136137

137-
# if isinstance(database_connection_or_url, str):
138-
# self.database_connection = create_engine(database_connection_or_url)
139-
# else:
140-
# self.database_connection = database_connection_or_url
141-
142-
# self.database_exists = database_exists(self.database_connection.url)
143-
# if force_recreate:
144-
# self._create_database()
145-
146138
if metadata:
147139
self.metadata = metadata
148140
else:
@@ -161,44 +153,21 @@ def __init__(
161153
if instantiate_database:
162154
if not self.db_backend.database_exists:
163155
self.db_backend._create_database()
156+
# else:
157+
# self.clear_tables()
164158
self.db_backend._create_schemas()
165-
self._instantiate_database()
159+
self._instantiate_database(print_sql)
166160

167-
# def _create_schemas(self):
168-
# with self.database_connection.begin() as trans:
169-
# trans.execute(CreateSchema("common", if_not_exists=True))
170-
# trans.execute(CreateSchema("sdo", if_not_exists=True))
171-
# trans.execute(CreateSchema("sco", if_not_exists=True))
172-
# trans.execute(CreateSchema("sro", if_not_exists=True))
173-
174-
def _instantiate_database(self):
161+
def _instantiate_database(self, print_sql=False):
175162
self.metadata.create_all(self.db_backend.database_connection)
176-
177-
# def _create_database(self):
178-
# if self.database_exists:
179-
# drop_database(self.database_connection.url)
180-
# create_database(self.database_connection.url)
181-
# self.database_exists = database_exists(self.database_connection.url)
182-
183-
def generate_stix_schema(self):
184-
for t in self.metadata.tables.values():
185-
print(CreateTable(t).compile(self.db_backend.database_connection))
163+
if print_sql:
164+
for t in self.metadata.tables.values():
165+
print(CreateTable(t).compile(self.db_backend.database_connection))
186166

187167
def add(self, stix_data, version=None):
188168
_add(self, stix_data)
189169
add.__doc__ = _add.__doc__
190170

191-
# @staticmethod
192-
# def _determine_schema_name(stix_object):
193-
# if isinstance(stix_object, _DomainObject):
194-
# return "sdo"
195-
# elif isinstance(stix_object, _Observable):
196-
# return "sco"
197-
# elif isinstance(stix_object, _RelationshipObject):
198-
# return "sro"
199-
# elif isinstance(stix_object, _MetaObject):
200-
# return "common"
201-
202171
def insert_object(self, stix_object):
203172
schema_name = self.db_backend.determine_schema_name(stix_object)
204173
with self.db_backend.database_connection.begin() as trans:

stix2/datastore/relational_db/relational_db_testing.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,15 +254,16 @@ def custom_obj():
254254

255255
def main():
256256
store = RelationalDBStore(
257-
PostgresBackend("postgresql://localhost/stix-data-sink"),
257+
PostgresBackend("postgresql://localhost/stix-data-sink", force_recreate=True),
258258
False,
259259
None,
260260
True,
261+
print_sql=True,
261262
)
262263

263264
if store.sink.db_backend.database_exists:
264-
store.sink.generate_stix_schema()
265-
store.sink.clear_tables()
265+
# store.sink.generate_stix_schema()
266+
# store.sink.clear_tables()
266267

267268
# td = test_dictionary()
268269

@@ -295,8 +296,8 @@ def main():
295296
dict_example = dictionary_test()
296297
store.add(dict_example)
297298

298-
read_obj = store.get(directory_stix_object.id)
299-
print(read_obj)
299+
# read_obj = store.get(directory_stix_object.id)
300+
# print(read_obj)
300301
else:
301302
print("database does not exist")
302303

stix2/datastore/relational_db/table_creation.py

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ def aux_table_property(prop, name, core_properties):
3232
return False
3333

3434

35-
def create_array_column(property_name, contained_sql_type):
35+
def create_array_column(property_name, contained_sql_type, optional):
3636
return Column(
3737
property_name,
3838
ARRAY(contained_sql_type),
39-
CheckConstraint(f"array_length({property_name}, 1) IS NOT NULL"),
40-
nullable=False,
39+
CheckConstraint(f"{property_name} IS NULL or array_length({property_name}, 1) IS NOT NULL"),
40+
nullable=optional
4141
)
4242

4343

@@ -47,12 +47,12 @@ def create_array_child_table(metadata, db_backend, table_name, property_name, co
4747
Column(
4848
"id",
4949
db_backend.determine_sql_type_for_key_as_id(),
50-
ForeignKey(
51-
canonicalize_table_name(table_name, schema_name) + ".id",
52-
ondelete="CASCADE",
53-
),
50+
# ForeignKey(
51+
# canonicalize_table_name(table_name, schema_name) + ".id",
52+
# ondelete="CASCADE",
53+
# ),
5454
nullable=False,
55-
primary_key=True,
55+
#primary_key=True,
5656
),
5757
Column(
5858
property_name,
@@ -178,7 +178,7 @@ def create_granular_markings_table(metadata, db_backend, sco_or_sdo):
178178
db_backend.determine_sql_type_for_key_as_id(),
179179
ForeignKey(canonicalize_table_name("core_" + sco_or_sdo, schema_name) + ".id", ondelete="CASCADE"),
180180
nullable=False,
181-
primary_key=True,
181+
# primary_key=not db_backend.array_allowed(),
182182
),
183183
Column("lang", db_backend.determine_sql_type_for_string_property()),
184184
Column(
@@ -191,35 +191,61 @@ def create_granular_markings_table(metadata, db_backend, sco_or_sdo):
191191
),
192192
]
193193
if db_backend.array_allowed():
194-
child_table = False
195-
columns.append(create_array_column("selectors", db_backend.determine_sql_type_for_string_property()))
196-
else:
197-
child_table = True
198-
199-
tables = [
200-
Table(
201-
canonicalize_table_name("granular_marking_" + sco_or_sdo),
202-
metadata,
203-
*columns,
204-
CheckConstraint(
205-
"""(lang IS NULL AND marking_ref IS NOT NULL)
206-
OR
207-
(lang IS NOT NULL AND marking_ref IS NULL)""",
208-
),
209-
schema=schema_name
210-
),
211-
]
212-
if child_table:
213-
tables.append(
214-
create_array_child_table(
194+
columns.append(create_array_column("selectors", db_backend.determine_sql_type_for_string_property(), False))
195+
return [
196+
Table(
197+
"granular_marking_" + sco_or_sdo,
215198
metadata,
216-
db_backend,
199+
*columns,
200+
CheckConstraint(
201+
"""(lang IS NULL AND marking_ref IS NOT NULL)
202+
OR
203+
(lang IS NOT NULL AND marking_ref IS NULL)""",
204+
),
205+
schema=schema_name
206+
),
207+
]
208+
else:
209+
columns.append(Column(
210+
"selectors",
211+
db_backend.determine_sql_type_for_key_as_int(),
212+
unique=True
213+
)
214+
)
215+
tables = [
216+
Table(
217217
"granular_marking_" + sco_or_sdo,
218+
metadata,
219+
*columns,
220+
CheckConstraint(
221+
"""(lang IS NULL AND marking_ref IS NOT NULL)
222+
OR
223+
(lang IS NOT NULL AND marking_ref IS NULL)""",
224+
),
225+
schema=schema_name
226+
),
227+
]
228+
schema_name = db_backend.schema_for_core()
229+
columns = [
230+
Column(
231+
"id",
232+
db_backend.determine_sql_type_for_key_as_int(),
233+
ForeignKey(
234+
canonicalize_table_name("granular_marking_" + sco_or_sdo, schema_name) + ".selectors",
235+
ondelete="CASCADE",
236+
),
237+
# primary_key=True,
238+
nullable=False,
239+
),
240+
Column(
218241
"selector",
219242
db_backend.determine_sql_type_for_string_property(),
243+
nullable=False,
220244
),
221-
)
222-
return tables
245+
]
246+
tables.append(Table(canonicalize_table_name("granular_marking_" + sco_or_sdo + "_" + "selector"),
247+
metadata, *columns, schema=schema_name))
248+
return tables
223249

224250

225251
def create_external_references_tables(metadata, db_backend):
@@ -275,7 +301,7 @@ def create_core_table(metadata, db_backend, schema_name):
275301
]
276302
columns.extend(sdo_columns)
277303
if db_backend.array_allowed():
278-
columns.append(create_array_column("labels", db_backend.determine_sql_type_for_string_property())),
304+
columns.append(create_array_column("labels", db_backend.determine_sql_type_for_string_property(), True))
279305
else:
280306
columns.append(Column("defanged", db_backend.determine_sql_type_for_boolean_property(), default=False))
281307

stix2/datastore/relational_db/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def determine_sql_type_from_stix(cls_or_inst, db_backend): # noqa: F811
148148
elif is_class_or_instance(cls_or_inst, IntegerProperty):
149149
return db_backend.determine_sql_type_for_integer_property()
150150
elif is_class_or_instance(cls_or_inst, StringProperty):
151-
return db_backend.determine_sql_type_for_integer_property()
151+
return db_backend.determine_sql_type_for_string_property()
152152
elif is_class_or_instance(cls_or_inst, ReferenceProperty):
153153
db_backend.determine_sql_type_for_reference_property()
154154
elif is_class_or_instance(cls_or_inst, TimestampProperty):

0 commit comments

Comments
 (0)