Skip to content

Commit ad36667

Browse files
Merge pull request #386 from datajoint/stage
Bug fix for del() with multiple Foreign Key aliases to the same Primary.
2 parents 3ced068 + 8eec5e9 commit ad36667

File tree

11 files changed

+114
-34
lines changed

11 files changed

+114
-34
lines changed

+dj/+internal/Header.m

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,25 @@ function project(self, params)
152152

153153
include = [self.attributes.iskey]; % always include the primary key
154154
for iAttr=1:length(params)
155-
if strcmp('*',params{iAttr})
155+
if strcmp('*', params{iAttr})
156156
include = include | true; % include all attributes
157157
else
158158
% process a renamed attribute
159159
toks = regexp(params{iAttr}, ...
160160
'^([a-z]\w*)\s*->\s*(\w+)', 'tokens');
161161
if ~isempty(toks)
162-
ix = find(strcmp(toks{1}{1},self.names));
163-
assert(length(ix)==1,'Attribute `%s` not found',toks{1}{1});
164-
assert(~ismember(toks{1}{2},union({self.attributes.alias}, ...
165-
self.names)), 'Duplicate attribute alias `%s`',toks{1}{2})
162+
ix = find(strcmp(toks{1}{1}, self.names));
163+
if ~length(ix)
164+
ix = find(strcmp(toks{1}{1}, {self.attributes.alias}));
165+
assert(length(ix)==1, 'Attribute `%s` not found', toks{1}{1});
166+
self.attributes(self.count + 1) = self.attributes(ix);
167+
self.attributes(self.count).name = self.attributes(self.count).alias;
168+
self.attributes(self.count).alias = '';
169+
ix = self.count;
170+
end
171+
assert(length(ix)==1, 'Attribute `%s` not found', toks{1}{1});
172+
assert(~ismember(toks{1}{2}, union({self.attributes.alias}, ...
173+
self.names)), 'Duplicate attribute alias `%s`', toks{1}{2})
166174
self.attributes(ix).name = toks{1}{2};
167175
self.attributes(ix).alias = toks{1}{1};
168176
else

+dj/Relvar.m

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,23 +92,22 @@ function cleanup(self)
9292
@(x) strcmp(x.from, rels(ix).fullTableName), ...
9393
self.schema.conn.foreignKeys, 'uni', true);
9494
fks = self.schema.conn.foreignKeys(fk_index);
95-
if ~fks.aliased
95+
if ~all(fks.aliased)
9696
% If matched foreign keys are not aliased, no renaming
9797
% necessary. Restrict table based on normal projection.
9898
rels(ix).restrict(proj(rels(i)));
9999
else
100100
% 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);
101+
fks_ref_attrs_flattened = arrayfun(@(x) x.ref_attrs{:}, fks, 'UniformOutput', false);
102+
fks_attrs_flattened = arrayfun(@(x) x.attrs{:}, fks, 'UniformOutput', false);
103+
% Build OR string query using original and renamed attributes
104+
or_string_query = strjoin(cellfun(...
105+
@(attr,ref_attr) sprintf('%s = "%s"', attr, rels(i).fetch1(ref_attr)), ...
106+
fks_attrs_flattened, fks_ref_attrs_flattened, ...
107+
'uni', 0), ' OR ');
109108
% Restrict table based on projection with rename arguments on
110109
% foreign keys.
111-
rels(ix).restrict(proj(rels(i), aliased_attrs{:}));
110+
rels(ix).restrict(or_string_query);
112111
end
113112
else
114113
rels(ix).restrict(rels(i).restrictions{:});

.github/workflows/development.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ jobs:
1616
matrix:
1717
matlab_version: ["R2019a"]
1818
mysql_version: ["8.0.18", "5.7", "5.6"]
19-
include:
20-
- matlab_version: "R2018b"
21-
mysql_version: "5.7"
22-
- matlab_version: "R2016b"
23-
mysql_version: "5.7"
19+
# include:
20+
# - matlab_version: "R2018b"
21+
# mysql_version: "5.7"
22+
# - matlab_version: "R2016b"
23+
# mysql_version: "5.7"
2424
steps:
2525
- uses: actions/checkout@v2
2626
- name: Run primary tests
@@ -31,7 +31,7 @@ jobs:
3131
MATLAB_HOSTID: ${{ secrets.matlab_hostid }}
3232
MATLAB_VERSION: ${{ matrix.matlab_version }}
3333
MYSQL_TAG: ${{ matrix.mysql_version }}
34-
MINIO_VER: RELEASE.2019-09-26T19-42-35Z
34+
MINIO_VER: RELEASE.2022-01-03T18-22-58Z
3535
MATLAB_LICENSE: ${{ secrets[format('matlab_license_{0}', matrix.matlab_version)] }}
3636
DOCKER_CLIENT_TIMEOUT: "120"
3737
COMPOSE_HTTP_TIMEOUT: "120"

LNX-docker-compose.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ services:
2929
interval: 1s
3030
fakeservices.datajoint.io:
3131
<<: *net
32-
image: datajoint/nginx:v0.0.16
32+
image: datajoint/nginx:v0.1.1
3333
environment:
3434
- ADD_db_TYPE=DATABASE
3535
- ADD_db_ENDPOINT=db:3306

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,14 @@ For help in utilizing `dj.config` (added in `3.4.0`), you may access the help vi
4242

4343
* Create an `.env` with desired development environment values e.g.
4444
``` sh
45-
MATLAB_USER=raphael
46-
MATLAB_LICENSE="#\ BEGIN----...---------END" # For image usage instructions see https://github.com/guzman-raphael/matlab, https://hub.docker.com/r/raphaelguzman/matlab
47-
MATLAB_VERSION=R2018b
45+
MATLAB_USER=rguzman
46+
MATLAB_LICENSE=IyBCRUd... # For image usage instructions see https://github.com/guzman-raphael/matlab, https://hub.docker.com/r/raphaelguzman/matlab
47+
MATLAB_VERSION=R2019a
4848
MATLAB_HOSTID=XX:XX:XX:XX:XX:XX
4949
MATLAB_UID=1000
5050
MATLAB_GID=1000
5151
MYSQL_TAG=5.7
52+
MINIO_VER=RELEASE.2022-01-03T18-22-58Z
5253
```
5354
* `cp local-docker-compose.yaml docker-compose.yaml`
5455
* `docker-compose up` (Note configured `JUPYTER_PASSWORD`)

local-docker-compose.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ services:
3434
interval: 1s
3535
fakeservices.datajoint.io:
3636
<<: *net
37-
image: datajoint/nginx:v0.0.16
37+
image: datajoint/nginx:v0.1.1
3838
environment:
3939
- ADD_db_TYPE=DATABASE
4040
- ADD_db_ENDPOINT=db:3306
@@ -78,11 +78,12 @@ services:
7878
ports:
7979
- "8888:8888"
8080
user: ${MATLAB_UID}:${MATLAB_GID}
81-
working_dir: /home/muser/notebooks
81+
working_dir: /home/muser
8282
command:
8383
- /bin/bash
8484
- -c
8585
- |
86+
set -e
8687
ORIG_DIR=$$(pwd)
8788
mkdir ~/Documents
8889
cd /src
@@ -121,7 +122,7 @@ services:
121122
"
122123
cd "$${ORIG_DIR}"
123124
# Copy preferences
124-
cp /tmp/matlab.prf /home/muser/.matlab/${MATLAB_VERSION}/matlab.prf
125+
# cp /tmp/matlab.prf /home/muser/.matlab/${MATLAB_VERSION}/matlab.prf
125126
# Interactive Jupyter Notebook environment
126127
jupyter notebook
127128
mac_address: $MATLAB_HOSTID

tests/TestDelete.m

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,33 @@ function TestDelete_testRenamedDelete(testCase)
3636
testCase.verifyEqual(...
3737
length(fetch(Company.Machine & struct('employee_id', 'shan'))), 0);
3838
end
39+
function TestDelete_testTwoFKOnePK(testCase)
40+
st = dbstack;
41+
disp(['---------------' st(1).name '---------------']);
42+
% https:%github.com/datajoint/datajoint-matlab/issues/379
43+
dj.config('safemode', false);
44+
package = 'TestLab';
45+
46+
dj.createSchema(package,[testCase.test_root '/test_schemas'], ...
47+
[testCase.PREFIX '_testlab']);
48+
49+
users = [{'user0'; 'user1'; 'user2'}];
50+
51+
insert(TestLab.User, users);
52+
53+
duty = [{'2020-01-01','user0','user1'},
54+
{'2020-01-02','user1','user2'},
55+
{'2020-12-31','user0','user2'}];
56+
57+
insert(TestLab.Duty, duty);
58+
59+
key.user_id = 'user1';
60+
del(TestLab.User & key);
61+
62+
testCase.verifyEqual(length(fetch(TestLab.User)), 2);
63+
testCase.verifyEqual(length(fetch(TestLab.Duty)), 1);
64+
testCase.verifyEqual(length(fetch(TestLab.User & 'user_id = "user1"')), 0);
65+
testCase.verifyEqual(length(fetch(TestLab.Duty & 'duty_first = "user1" OR duty_second = "user1"')), 0);
66+
end
3967
end
4068
end

tests/TestExternalS3.m

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
classdef TestExternalS3 < Prep
22
% TestExternalS3 tests scenarios related to external S3 store.
33
methods (Test)
4-
function TestExternalS3_testRemote(testCase)
5-
st = dbstack;
6-
disp(['---------------' st(1).name '---------------']);
7-
TestExternalFile.TestExternalFile_checks(testCase, 'new_remote', ...
8-
'blobCache');
9-
end
4+
% function TestExternalS3_testRemote(testCase)
5+
% st = dbstack;
6+
% disp(['---------------' st(1).name '---------------']);
7+
% TestExternalFile.TestExternalFile_checks(testCase, 'new_remote', ...
8+
% 'blobCache');
9+
% end
1010
function TestExternalS3_testRemoteDefault(testCase)
1111
st = dbstack;
1212
disp(['---------------' st(1).name '---------------']);

tests/TestProjection.m

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,34 @@ function TestProjection_testDateConversion(testCase)
2525

2626
res = q.fetch1('enrolled_date');
2727
testCase.verifyEqual(res, '2018-01-24');
28+
29+
dj.config('safemode', 0);
30+
drop(University.Student);
31+
end
32+
function TestProjection_testRenameSameKey(testCase)
33+
st = dbstack;
34+
disp(['---------------' st(1).name '---------------']);
35+
package = 'University';
36+
37+
c1 = dj.conn(...
38+
testCase.CONN_INFO.host,...
39+
testCase.CONN_INFO.user,...
40+
testCase.CONN_INFO.password,'',true);
41+
42+
dj.createSchema(package,[testCase.test_root '/test_schemas'], ...
43+
[testCase.PREFIX '_university']);
44+
45+
insert(University.Student, {
46+
10 'Raphael' 'Guzman' datestr(datetime, 'yyyy-mm-dd HH:MM:SS')
47+
11 'Shan' 'Shen' '2019-11-25 12:34:56'
48+
12 'Joe' 'Schmoe' '2018-01-24 14:34:16'
49+
});
50+
51+
q = proj(University.Student & 'first_name = "Raphael"', 'student_id->faculty_id', 'student_id->school_id');
52+
testCase.verifyEqual(q.fetch('faculty_id', 'school_id'), struct('faculty_id', 10, 'school_id', 10));
53+
54+
dj.config('safemode', 0);
55+
drop(University.Student);
2856
end
2957
end
3058
end

tests/test_schemas/+TestLab/Duty.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
%{
2+
duty_date: date
3+
-----
4+
(duty_first) -> TestLab.User(user_id)
5+
(duty_second) -> TestLab.User(user_id)
6+
%}
7+
8+
classdef Duty < dj.Manual
9+
end

0 commit comments

Comments
 (0)