Skip to content

Commit 58b0f2c

Browse files
author
Adam Ever-Hadani
authored
Merge pull request #25 from RDFLib/feature/ah-literals-lang-key
Fix unique constraints to take into account object language tag for literal / quoted statements
2 parents 6ac033d + 570d5aa commit 58b0f2c

File tree

4 files changed

+45
-39
lines changed

4 files changed

+45
-39
lines changed

rdflib_sqlalchemy/base.py

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ def _build_type_sql_command(self, member, klass, context):
2121
"member": member,
2222
"klass": klass,
2323
"context": context.identifier,
24-
"termComb": int(type_to_term_combination(member, klass, context))}
24+
"termComb": int(type_to_term_combination(member, klass, context))
25+
}
2526

2627
def _build_literal_triple_sql_command(self, subject, predicate, obj, context):
2728
"""
@@ -70,10 +71,8 @@ def _build_triple_sql_command(self, subject, predicate, obj, context, quoted):
7071
"object": obj,
7172
"context": context.identifier,
7273
"termComb": triple_pattern,
73-
"objLanguage": isinstance(
74-
obj, Literal) and obj.language or None,
75-
"objDatatype": isinstance(
76-
obj, Literal) and obj.datatype or None
74+
"objLanguage": isinstance(obj, Literal) and obj.language or None,
75+
"objDatatype": isinstance(obj, Literal) and obj.datatype or None
7776
}
7877
else:
7978
params = {
@@ -89,18 +88,18 @@ def build_clause(self, table, subject, predicate, obj, context=None, typeTable=F
8988
"""Build WHERE clauses for the supplied terms and, context."""
9089
if typeTable:
9190
clauseList = [
92-
self.buildTypeMemberClause(subject, table),
93-
self.buildTypeClassClause(obj, table),
94-
self.buildContextClause(context, table)
91+
self.build_type_member_clause(subject, table),
92+
self.build_type_class_clause(obj, table),
93+
self.build_context_clause(context, table)
9594
]
9695
else:
9796
clauseList = [
98-
self.buildSubjClause(subject, table),
99-
self.buildPredClause(predicate, table),
100-
self.buildObjClause(obj, table),
101-
self.buildContextClause(context, table),
102-
self.buildLitDTypeClause(obj, table),
103-
self.buildLitLanguageClause(obj, table)
97+
self.build_subject_clause(subject, table),
98+
self.build_predicate_clause(predicate, table),
99+
self.build_object_clause(obj, table),
100+
self.build_context_clause(context, table),
101+
self.build_literal_datatype_clause(obj, table),
102+
self.build_literal_language_clause(obj, table)
104103
]
105104

106105
clauseList = [clause for clause in clauseList if clause is not None]
@@ -109,14 +108,14 @@ def build_clause(self, table, subject, predicate, obj, context=None, typeTable=F
109108
else:
110109
return None
111110

112-
def buildLitDTypeClause(self, obj, table):
111+
def build_literal_datatype_clause(self, obj, table):
113112
"""Build Literal and datatype clause."""
114113
if isinstance(obj, Literal) and obj.datatype is not None:
115114
return table.c.objDatatype == obj.datatype
116115
else:
117116
return None
118117

119-
def buildLitLanguageClause(self, obj, table):
118+
def build_literal_language_clause(self, obj, table):
120119
"""Build Literal and language clause."""
121120
if isinstance(obj, Literal) and obj.language is not None:
122121
return table.c.objLanguage == obj.language
@@ -126,25 +125,25 @@ def buildLitLanguageClause(self, obj, table):
126125
# Where Clause utility Functions
127126
# The predicate and object clause builders are modified in order
128127
# to optimize subjects and objects utility functions which can
129-
# take lists as their last argument (object, predicate -
130-
# respectively)
131-
def buildSubjClause(self, subject, table):
128+
# take lists as their last argument (object, predicate - respectively)
129+
130+
def build_subject_clause(self, subject, table):
132131
"""Build Subject clause."""
133132
if isinstance(subject, REGEXTerm):
134133
# TODO: this work only in mysql. Must adapt for postgres and sqlite
135134
return table.c.subject.op("REGEXP")(subject)
136135
elif isinstance(subject, list):
137136
# clauseStrings = [] --- unused
138137
return expression.or_(
139-
*[self.buildSubjClause(s, table) for s in subject if s])
138+
*[self.build_subject_clause(s, table) for s in subject if s])
140139
elif isinstance(subject, (QuotedGraph, Graph)):
141140
return table.c.subject == subject.identifier
142141
elif subject is not None:
143142
return table.c.subject == subject
144143
else:
145144
return None
146145

147-
def buildPredClause(self, predicate, table):
146+
def build_predicate_clause(self, predicate, table):
148147
"""
149148
Build Predicate clause.
150149
@@ -157,13 +156,13 @@ def buildPredClause(self, predicate, table):
157156
return table.c.predicate.op("REGEXP")(predicate)
158157
elif isinstance(predicate, list):
159158
return expression.or_(
160-
*[self.buildPredClause(p, table) for p in predicate if p])
159+
*[self.build_predicate_clause(p, table) for p in predicate if p])
161160
elif predicate is not None:
162161
return table.c.predicate == predicate
163162
else:
164163
return None
165164

166-
def buildObjClause(self, obj, table):
165+
def build_object_clause(self, obj, table):
167166
"""
168167
Build Object clause.
169168
@@ -176,15 +175,15 @@ def buildObjClause(self, obj, table):
176175
return table.c.object.op("REGEXP")(obj)
177176
elif isinstance(obj, list):
178177
return expression.or_(
179-
*[self.buildObjClause(o, table) for o in obj if o])
178+
*[self.build_object_clause(o, table) for o in obj if o])
180179
elif isinstance(obj, (QuotedGraph, Graph)):
181180
return table.c.object == obj.identifier
182181
elif obj is not None:
183182
return table.c.object == obj
184183
else:
185184
return None
186185

187-
def buildContextClause(self, context, table):
186+
def build_context_clause(self, context, table):
188187
"""Build Context clause."""
189188
if isinstance(context, REGEXTerm):
190189
# TODO: this work only in mysql. Must adapt for postgres and sqlite
@@ -194,27 +193,27 @@ def buildContextClause(self, context, table):
194193
else:
195194
return None
196195

197-
def buildTypeMemberClause(self, subject, table):
196+
def build_type_member_clause(self, subject, table):
198197
"""Build Type Member clause."""
199198
if isinstance(subject, REGEXTerm):
200199
# TODO: this work only in mysql. Must adapt for postgres and sqlite
201200
return table.c.member.op("regexp")(subject)
202201
elif isinstance(subject, list):
203202
return expression.or_(
204-
*[self.buildTypeMemberClause(s, table) for s in subject if s])
203+
*[self.build_type_member_clause(s, table) for s in subject if s])
205204
elif subject is not None:
206205
return table.c.member == subject
207206
else:
208207
return None
209208

210-
def buildTypeClassClause(self, obj, table):
209+
def build_type_class_clause(self, obj, table):
211210
"""Build Type Class clause."""
212211
if isinstance(obj, REGEXTerm):
213212
# TODO: this work only in mysql. Must adapt for postgres and sqlite
214213
return table.c.klass.op("regexp")(obj)
215214
elif isinstance(obj, list):
216215
return expression.or_(
217-
*[self.buildTypeClassClause(o, table) for o in obj if o])
216+
*[self.build_type_class_clause(o, table) for o in obj if o])
218217
elif obj is not None:
219218
return obj and table.c.klass == obj
220219
else:

rdflib_sqlalchemy/store.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@
4848
Any = None
4949

5050

51+
def generate_interned_id(identifier):
52+
return "{prefix}{identifier_hash}".format(
53+
prefix=INTERNED_PREFIX,
54+
identifier_hash=hashlib.sha1(identifier.encode("utf8")).hexdigest()[:10],
55+
)
56+
57+
5158
class SQLAlchemy(Store, SQLGeneratorMixin, StatisticsMixin):
5259
"""
5360
SQL-92 formula-aware implementation of an rdflib Store.
@@ -84,10 +91,7 @@ def __init__(self, identifier=None, configuration=None, engine=None):
8491
self.engine = engine
8592

8693
# Use only the first 10 bytes of the digest
87-
self._interned_id = "{prefix}{identifier_hash}".format(
88-
prefix=INTERNED_PREFIX,
89-
identifier_hash=hashlib.sha1(self.identifier.encode("utf8")).hexdigest()[:10],
90-
)
94+
self._interned_id = generate_interned_id(self.identifier)
9195

9296
# This parameter controls how exlusively the literal table is searched
9397
# If true, the Literal partition is searched *exclusively* if the
@@ -159,10 +163,10 @@ def __len__(self, context=None):
159163
asserted = expression.alias(asserted_table, "asserted")
160164
literal = expression.alias(literal_table, "literal")
161165

162-
quotedContext = self.buildContextClause(context, quoted)
163-
assertedContext = self.buildContextClause(context, asserted)
164-
typeContext = self.buildContextClause(context, typetable)
165-
literalContext = self.buildContextClause(context, literal)
166+
quotedContext = self.build_context_clause(context, quoted)
167+
assertedContext = self.build_context_clause(context, asserted)
168+
typeContext = self.build_context_clause(context, typetable)
169+
literalContext = self.build_context_clause(context, literal)
166170

167171
if context is not None:
168172
selects = [
@@ -745,7 +749,7 @@ def _remove_context(self, identifier):
745749
try:
746750
for table in [quoted_table, asserted_table,
747751
asserted_type_table, literal_table]:
748-
clause = self.buildContextClause(identifier, table)
752+
clause = self.build_context_clause(identifier, table)
749753
connection.execute(table.delete(clause))
750754
trans.commit()
751755
except Exception:

rdflib_sqlalchemy/tables.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def create_literal_statements_table(interned_id, metadata):
142142
"subject",
143143
"predicate",
144144
"object",
145+
"objLanguage",
145146
"context",
146147
unique=True,
147148
mysql_length=MYSQL_MAX_INDEX_LENGTH,
@@ -190,6 +191,7 @@ def create_quoted_statements_table(interned_id, metadata):
190191
"subject",
191192
"predicate",
192193
"object",
194+
"objLanguage",
193195
"context",
194196
unique=True,
195197
mysql_length=MYSQL_MAX_INDEX_LENGTH,

rdflib_sqlalchemy/termutils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ def extract_triple(tupleRt, store, hardCodedContext=None):
189189
190190
Take a tuple which represents an entry in a result set and
191191
converts it to a tuple of terms using the termComb integer
192-
to interpret how to instantiate each term
192+
to interpret how to instantiate each term.
193+
193194
"""
194195
try:
195196
id, subject, predicate, obj, rtContext, termComb, \

0 commit comments

Comments
 (0)