Skip to content

Commit 3ced068

Browse files
authored
Merge pull request #369 from datajoint/stage
Fix bugs with dj.config loading, fetchn, and cascading delete with renamed attributes
2 parents 82183f1 + 7fe14f5 commit 3ced068

File tree

14 files changed

+132
-12
lines changed

14 files changed

+132
-12
lines changed

+dj/+internal/GeneralRelvar.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ function clip(self)
290290
% if renamed, use the renamed attribute
291291
name = regexp(specs{iArg}, '(\w+)\s*$', 'tokens');
292292
sel = cellfun(@(x) strcmp(x, name{1}{1}), {self.header.attributes.name});
293-
if self.tableHeader.attributes(sel).isNumeric
293+
if self.header.attributes(sel).isNumeric
294294
varargout{iArg} = [s.(name{1}{1})]';
295295
else
296296
varargout{iArg} = {s.(name{1}{1})}';

+dj/+internal/Settings.m

+7-2
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,17 @@ function envVarUpdate()
148148
out = STATE;
149149
if any(strcmpi(operation, {'set', 'load'}))
150150
if isempty(STATE)
151-
STATE = new;
151+
STATE = rmfield(dj.internal.Settings.DEFAULTS, intersect(fieldnames( ...
152+
dj.internal.Settings.DEFAULTS), fieldnames(new)));
153+
names = [fieldnames(STATE); fieldnames(new)];
154+
STATE = orderfields(...
155+
cell2struct([struct2cell(STATE); struct2cell(new)], names, 1));
152156
else
153157
% merge with existing STATE
154158
STATE = rmfield(STATE, intersect(fieldnames(STATE), fieldnames(new)));
155159
names = [fieldnames(STATE); fieldnames(new)];
156-
STATE = orderfields(cell2struct([struct2cell(STATE); struct2cell(new)], names, 1));
160+
STATE = orderfields(...
161+
cell2struct([struct2cell(STATE); struct2cell(new)], names, 1));
157162
end
158163
if strcmpi(operation, 'load')
159164
envVarUpdate();

+dj/Relvar.m

+23-3
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,29 @@ function cleanup(self)
8787
self.schema.conn.tableToClass(child),list)), rels(i).children)
8888
% and restrict them by it or its restrictions
8989
if restrictByMe(i)
90-
% TODO: handle renamed attributes self.conn.foreignKeys( ...
91-
% fullTableName).aliased
92-
rels(ix).restrict(pro(rels(i)))
90+
% Extract foreign key indices for table that match target parent
91+
fk_index = arrayfun(...
92+
@(x) strcmp(x.from, rels(ix).fullTableName), ...
93+
self.schema.conn.foreignKeys, 'uni', true);
94+
fks = self.schema.conn.foreignKeys(fk_index);
95+
if ~fks.aliased
96+
% If matched foreign keys are not aliased, no renaming
97+
% necessary. Restrict table based on normal projection.
98+
rels(ix).restrict(proj(rels(i)));
99+
else
100+
% Determine which foreign keys have been renamed
101+
alias_index = cellfun(...
102+
@(ref_attr, attr) ~strcmp(ref_attr, attr), ...
103+
fks.ref_attrs, fks.attrs, 'uni', true);
104+
% Create rename arguments for projection
105+
aliased_attrs = cellfun(...
106+
@(ref_attr, attr) sprintf('%s->%s', ref_attr, attr), ...
107+
fks.ref_attrs(alias_index), fks.attrs(alias_index), ...
108+
'uni', false);
109+
% Restrict table based on projection with rename arguments on
110+
% foreign keys.
111+
rels(ix).restrict(proj(rels(i), aliased_attrs{:}));
112+
end
93113
else
94114
rels(ix).restrict(rels(i).restrictions{:});
95115
end

+dj/struct.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@
122122
s = dj.struct.proj(s, varargin{:});
123123
end
124124

125-
function s = proj(s,varargin)
125+
function s = proj(s, varargin)
126126
% DJ.STRUCT.PROJ - the relational projection operator
127127
% of structure array onto the specified fields.
128128
% The result may contain duplicate tuples.

+dj/version.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
function varargout = version
22
% report DataJoint version
33

4-
v = struct('major',3,'minor',4,'bugfix',3);
4+
v = struct('major', 3, 'minor', 4, 'bugfix', 3);
55

66
if nargout
77
varargout{1}=v;

docs-parts/intro/Releases_lang1.rst

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
3.4.3 -- May 28, 2021
2-
---------------------
2+
--------------------------
3+
* Bugfix: `dj.config` omits default values when loading new config immediately after MATLAB boot (#359) PR #369
4+
* Bugfix: Regression error when using fetchn with a join (#361) PR #369
5+
* Bugfix: Cascading delete not functioning properly when using renamed foreign keys (#362) PR #369
6+
* Update NGINX reverse-proxy image use PR #369
37
* Bugfix: Add support to curly brackets in comments (#365) PR #373
48

59
3.4.2 -- March 16, 2021

tests/Main.m

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
classdef Main < ...
22
TestConfig & ...
33
TestConnection & ...
4+
TestDelete & ...
45
TestDeclaration & ...
56
TestERD & ...
67
TestExternalFile & ...

tests/TestConfig.m

+14-2
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,20 @@ function TestConfig_testLoad(testCase)
200200
% test load on launch MATLAB
201201
clear functions;
202202
dj.config.load(sprintf('%s/test_schemas/config_lite.json', pkg_path));
203-
% cleanup
204-
dj.config.restore;
203+
try
204+
port = dj.config('databasePort');
205+
testCase.verifyEqual(port, 3306);
206+
catch ME
207+
switch ME.identifier
208+
case 'DataJoint:Config:InvalidKey'
209+
% cleanup
210+
dj.config.restore;
211+
rethrow(ME);
212+
otherwise
213+
% cleanup
214+
dj.config.restore;
215+
end
216+
end
205217
end
206218
function TestConfig_testEnv(testCase)
207219
st = dbstack;

tests/TestDelete.m

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
classdef TestDelete < Prep
2+
% TestDelete tests delete operations.
3+
methods (Test)
4+
function TestDelete_testRenamedDelete(testCase)
5+
st = dbstack;
6+
disp(['---------------' st(1).name '---------------']);
7+
% https://github.com/datajoint/datajoint-matlab/issues/362
8+
dj.config('safemode', false);
9+
package = 'Company';
10+
11+
c1 = dj.conn(...
12+
testCase.CONN_INFO.host,...
13+
testCase.CONN_INFO.user,...
14+
testCase.CONN_INFO.password,'',true);
15+
16+
dj.createSchema(package,[testCase.test_root '/test_schemas'], ...
17+
[testCase.PREFIX '_company']);
18+
19+
inserti(Company.Employee, {'raphael', 2019; 'shan', 2018; 'chris', 2018; ...
20+
'thinh', 2019});
21+
inserti(Company.Duty, {'schedule1', 'shan', 2018; 'schedule2', 'raphael', 2019});
22+
inserti(Company.Machine, {'shan', 2018, 'abc1023'; 'raphael', 2019, 'xyz9876'});
23+
testCase.verifyEqual(length(fetch(Company.Employee)), 4);
24+
testCase.verifyEqual(length(fetch(Company.Duty)), 2);
25+
testCase.verifyEqual(length(fetch(Company.Machine)), 2);
26+
27+
del(Company.Employee & 'employee_id="shan"');
28+
29+
testCase.verifyEqual(length(fetch(Company.Employee)), 3);
30+
testCase.verifyEqual(...
31+
length(fetch(Company.Employee & struct('employee_id', 'shan'))), 0);
32+
testCase.verifyEqual(length(fetch(Company.Duty)), 1);
33+
testCase.verifyEqual(...
34+
length(fetch(Company.Duty & struct('monday_on_call', 'shan'))), 0);
35+
testCase.verifyEqual(length(fetch(Company.Machine)), 1);
36+
testCase.verifyEqual(...
37+
length(fetch(Company.Machine & struct('employee_id', 'shan'))), 0);
38+
end
39+
end
40+
end

tests/TestExternalFile.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function TestExternalFile_checks(test_instance, store, cache)
3939
'dimension_id', 4, ...
4040
'dimension', test_val1 ...
4141
));
42-
% check that external tables are loaded on new schema objs if they already exists
42+
% check that external tables are loaded on new schema objs if they already exist
4343
delete([test_instance.test_root '/test_schemas' '/+' package '/getSchema.m']);
4444
dj.createSchema(package,[test_instance.test_root '/test_schemas'], ...
4545
[test_instance.PREFIX '_external']);

tests/TestFetch.m

+18
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,24 @@ function TestFetch_testFetchn(testCase)
264264
testCase.verifyEqual(q.fetchn('number'), []);
265265
testCase.verifyEqual(q.fetchn('blob'), {});
266266
end
267+
function TestFetch_testFetchnJoin(testCase)
268+
st = dbstack;
269+
disp(['---------------' st(1).name '---------------']);
270+
% https://github.com/datajoint/datajoint-matlab/issues/361
271+
% https://github.com/datajoint/datajoint-matlab/issues/364
272+
package = 'Lab';
273+
274+
c1 = dj.conn(...
275+
testCase.CONN_INFO.host,...
276+
testCase.CONN_INFO.user,...
277+
testCase.CONN_INFO.password,'',true);
278+
279+
dj.createSchema(package,[testCase.test_root '/test_schemas'], ...
280+
[testCase.PREFIX '_lab']);
281+
282+
q = Lab.Session * Lab.Rig & 'rig_model="nonexistent"';
283+
testCase.verifyEqual(q.fetchn('rig_note'), {});
284+
end
267285
function TestFetch_testDescribe(testCase)
268286
st = dbstack;
269287
disp(['---------------' st(1).name '---------------']);

tests/test_schemas/+Company/Duty.m

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
%{
2+
schedule: varchar(32)
3+
---
4+
(monday_on_call) -> Company.Employee(employee_id)
5+
%}
6+
classdef Duty < dj.Manual
7+
end
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
%{
2+
#
3+
employee_id : varchar(12) #
4+
employment_year : int #
5+
%}
6+
classdef Employee < dj.Manual
7+
end

tests/test_schemas/+Company/Machine.m

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
%{
2+
-> Company.Employee
3+
machine_id: varchar(10)
4+
%}
5+
classdef Machine < dj.Manual
6+
end

0 commit comments

Comments
 (0)