Skip to content

Commit 853e2d2

Browse files
authored
Merge pull request #213 from datajoint/stage
Modify update to allow nullable updates for strings/date
2 parents f765b0e + 1dd9c55 commit 853e2d2

File tree

14 files changed

+612
-228
lines changed

14 files changed

+612
-228
lines changed

+dj/Relvar.m

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -343,40 +343,32 @@ function update(self, attrname, value)
343343
% update(v2p.Mice & key, 'mouse_dob', '2011-01-01')
344344
% update(v2p.Scan & key, 'lens') % set the value to NULL
345345

346-
assert(count(self)==1, 'Update is only allowed on one tuple at a time')
347-
isNull = nargin<3;
346+
assert(count(self)==1, 'Update is only allowed on one tuple at a time');
348347
header = self.header;
349348
ix = find(strcmp(attrname,header.names));
350-
assert(numel(ix)==1, 'invalid attribute name')
351-
assert(~header.attributes(ix).iskey, 'cannot update a key value. Use insert(..,''REPLACE'') instead')
349+
assert(numel(ix)==1, 'invalid attribute name');
350+
assert(~header.attributes(ix).iskey, ...
351+
'cannot update a key value. Use insert(..,''REPLACE'') instead');
352+
isNull = nargin<3 || (header.attributes(ix).isNumeric && isnan(value)) || ...
353+
(~header.attributes(ix).isNumeric && ~ischar(value) && isempty(value));
352354

353355
switch true
354356
case isNull
357+
assert(header.attributes(ix).isnullable, ...
358+
'attribute `%s` is not nullable.', attrname);
355359
valueStr = 'NULL';
356360
value = {};
357361
case header.attributes(ix).isString
358-
assert(dj.lib.isString(value), 'Value must be a string')
362+
assert(dj.lib.isString(value), 'Value must be a string');
359363
valueStr = '"{S}"';
360364
value = {char(value)};
361365
case header.attributes(ix).isBlob
362-
if isempty(value) && header.attributes(ix).isnullable
363-
valueStr = 'NULL';
364-
value = {};
365-
else
366-
valueStr = '"{M}"';
367-
value = {value};
368-
end
366+
valueStr = '"{M}"';
367+
value = {value};
369368
case header.attributes(ix).isNumeric
370369
assert(isscalar(value) && isnumeric(value), 'Numeric value must be scalar')
371-
if isnan(value)
372-
assert(header.attributes(ix).isnullable, ...
373-
'attribute `%s` is not nullable. NaNs not allowed', attrname)
374-
valueStr = 'NULL';
375-
value = {};
376-
else
377-
valueStr = sprintf('%1.16g',value);
378-
value = {};
379-
end
370+
valueStr = sprintf('%1.16g',value);
371+
value = {};
380372
otherwise
381373
error 'invalid update command'
382374
end

+dj/createSchema.m

Lines changed: 83 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,96 @@
11
function createSchema(package,parentdir,db)
2-
% DJ.CREATESCHEMA - interactively create a new DataJoint schema
3-
%
4-
% INPUT:
5-
% (optional) package - name of the package to be associated with the schema
6-
% (optional) parentdir - name of the dirctory where to create new package
7-
% (optional) db - database name to associate with the schema
8-
9-
if nargin < 3
10-
dbname = input('Enter database name >> ','s');
11-
else
12-
dbname = db;
13-
end
14-
15-
if ~dbname
16-
disp 'No database name entered. Quitting.'
17-
elseif isempty(regexp(dbname,'^[a-z][a-z0-9_]*$','once'))
18-
error 'Invalid database name. Begin with a letter, only lowercase alphanumerical and underscores.'
19-
else
20-
% create database
21-
s = query(dj.conn, ...
22-
sprintf('SELECT schema_name FROM information_schema.schemata WHERE schema_name = "%s"', dbname));
23-
24-
if ~isempty(s.schema_name)
25-
disp 'database already exists'
2+
% DJ.CREATESCHEMA - interactively create a new DataJoint schema
3+
%
4+
% INPUT:
5+
% (optional) package - name of the package to be associated with the schema
6+
% (optional) parentdir - name of the dirctory where to create new package
7+
% (optional) db - database name to associate with the schema
8+
9+
if nargin < 3
10+
dbname = input('Enter database name >> ','s');
2611
else
27-
query(dj.conn, sprintf('create schema %s',dbname))
28-
disp 'database created'
12+
dbname = db;
2913
end
30-
31-
if nargin < 1
32-
if usejava('desktop')
33-
disp 'Please select a package folder. Opening UI...'
34-
folder = uigetdir('./','Select a package folder');
14+
15+
if ~dbname
16+
disp 'No database name entered. Quitting.'
17+
elseif isempty(regexp(dbname,'^[a-z][a-z0-9_]*$','once'))
18+
error(['Invalid database name. Begin with a letter, only lowercase alphanumerical and ' ...
19+
'underscores.'])
20+
else
21+
% create database
22+
s = query(dj.conn, ...
23+
sprintf(['SELECT schema_name as `schema_name` ' ...
24+
'FROM information_schema.schemata WHERE schema_name = "%s"'], dbname));
25+
26+
if ~isempty(s.schema_name)
27+
disp 'database already exists'
3528
else
36-
folder = input('Enter package folder path >> ','s');
29+
query(dj.conn, sprintf('create schema %s',dbname))
30+
disp 'database created'
3731
end
38-
else
39-
if nargin < 3
32+
33+
if nargin < 1
4034
if usejava('desktop')
41-
fprintf('Please select folder to create package %s in. Opening UI...\n', ['+', package])
42-
folder = uigetdir('./', sprintf('Select folder to create package %s in', ['+', package]));
35+
disp 'Please select a package folder. Opening UI...'
36+
folder = uigetdir('./','Select a package folder');
4337
else
44-
folder = input('Enter parent folder path >> ','s');
38+
folder = input('Enter package folder path >> ','s');
4539
end
4640
else
47-
folder = parentdir;
48-
end
49-
50-
if folder
51-
folder = fullfile(folder, ['+', package]);
52-
mkdir(folder)
53-
end
54-
end
55-
56-
if ~folder
57-
disp 'No package selected. Cancelled.'
58-
else
59-
[filepath,package] = fileparts(folder);
60-
if package(1)~='+'
61-
error 'Package folders must start with a +'
41+
if nargin < 3
42+
if usejava('desktop')
43+
fprintf('Please select folder to create package %s in. Opening UI...\n', ...
44+
['+', package])
45+
folder = uigetdir('./', sprintf('Select folder to create package %s in', ...
46+
['+', package]));
47+
else
48+
folder = input('Enter parent folder path >> ','s');
49+
end
50+
else
51+
folder = parentdir;
52+
end
53+
54+
if folder
55+
folder = fullfile(folder, ['+', package]);
56+
mkdir(folder)
57+
end
6258
end
63-
package = package(2:end); % discard +
64-
65-
% create the getSchema function
66-
schemaFile = fullfile(folder,'getSchema.m');
67-
if exist(schemaFile,'file')
68-
fprintf('%s.getSchema.m already exists\n', package)
59+
60+
if ~folder
61+
disp 'No package selected. Cancelled.'
6962
else
70-
f = fopen(schemaFile,'wt');
71-
assert(-1 ~= f, 'Could not open %s', f)
72-
73-
fprintf(f,'function obj = getSchema\n');
74-
fprintf(f,'persistent schemaObject\n');
75-
fprintf(f,'if isempty(schemaObject)\n');
76-
fprintf(f,' schemaObject = dj.Schema(dj.conn, ''%s'', ''%s'');\n', package, dbname);
77-
fprintf(f,'end\n');
78-
fprintf(f,'obj = schemaObject;\n');
79-
fprintf(f,'end\n');
80-
fclose(f);
81-
end
82-
83-
% test that getSchema is on the path
84-
whichpath = which(sprintf('%s.getSchema',package));
85-
if isempty(whichpath)
86-
warning('Could not open %s.getSchema. Ensure that %s is on the path', package, filepath)
63+
[filepath,package] = fileparts(folder);
64+
if package(1)~='+'
65+
error 'Package folders must start with a +'
66+
end
67+
package = package(2:end); % discard +
68+
69+
% create the getSchema function
70+
schemaFile = fullfile(folder,'getSchema.m');
71+
if exist(schemaFile,'file')
72+
fprintf('%s.getSchema.m already exists\n', package)
73+
else
74+
f = fopen(schemaFile,'wt');
75+
assert(-1 ~= f, 'Could not open %s', f)
76+
77+
fprintf(f,'function obj = getSchema\n');
78+
fprintf(f,'persistent schemaObject\n');
79+
fprintf(f,'if isempty(schemaObject)\n');
80+
fprintf(f,' schemaObject = dj.Schema(dj.conn, ''%s'', ''%s'');\n', ...
81+
package, dbname);
82+
fprintf(f,'end\n');
83+
fprintf(f,'obj = schemaObject;\n');
84+
fprintf(f,'end\n');
85+
fclose(f);
86+
end
87+
88+
% test that getSchema is on the path
89+
whichpath = which(sprintf('%s.getSchema',package));
90+
if isempty(whichpath)
91+
warning('Could not open %s.getSchema. Ensure that %s is on the path', ...
92+
package, filepath)
93+
end
8794
end
8895
end
89-
end
96+
end

+tests/+lib/compareVersions.m

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
function res = compareVersions(verArray, verComp)
2+
% compareVersions - Semantic version comparison (greater than or equal)
3+
%
4+
% This function evaluates if an array of semantic versions is greater than
5+
% or equal to a reference version.
6+
%
7+
% DISTRIBUTION:
8+
% GitHub: https://github.com/guzman-raphael/compareVersions
9+
% FileExchange: https://www.mathworks.com/matlabcentral/fileexchange/71849-compareversions
10+
%
11+
% res = compareVersions(verArray, verComp)
12+
% INPUT:
13+
% verArray: Cell array with the following conditions:
14+
% - be of length >= 1,
15+
% - contain only string elements, and
16+
% - each element must be of length >= 1.
17+
% verComp: String or Char array that verArray will compare against for
18+
% greater than evaluation. Must be:
19+
% - be of length >= 1, and
20+
% - a string.
21+
% OUTPUT:
22+
% res: Logical array that identifies if each cell element in verArray
23+
% is greater than or equal to verComp.
24+
% TESTS:
25+
% Tests included for reference. From root package directory,
26+
% use command: runtests
27+
%
28+
% EXAMPLES:
29+
% output = compareVersions({'3.2.4beta','9.5.2.1','8.0'}, '8.0.0'); %logical([0 1 1])
30+
%
31+
% NOTES:
32+
% Tests included for reference. From root package directory,
33+
% use command: runtests
34+
%
35+
% Tested: Matlab 9.5.0.944444 (R2018b) Linux
36+
% Author: Raphael Guzman, DataJoint
37+
%
38+
% $License: MIT (use/copy/change/redistribute on own risk) $
39+
% $File: compareVersions.m $
40+
% History:
41+
% 001: 2019-06-12 11:00, First version.
42+
%
43+
% OPEN BUGS:
44+
% - None
45+
res_n = length(verArray);
46+
if ~res_n || max(cellfun(@(c) ~ischar(c) && ...
47+
~isstring(c),verArray)) > 0 || min(cellfun('length',verArray)) == 0
48+
msg = {
49+
'compareVersions:Error:CellArray'
50+
'Cell array to verify must:'
51+
'- be of length >= 1,'
52+
'- contain only string elements, and'
53+
'- each element must be of length >= 1.'
54+
};
55+
error('compareVersions:Error:CellArray', sprintf('%s\n',msg{:}));
56+
end
57+
if ~ischar(verComp) && ~isstring(verComp) || length(verComp) == 0
58+
msg = {
59+
'compareVersions:Error:VersionRef'
60+
'Version reference must:'
61+
'- be of length >= 1, and'
62+
'- a string.'
63+
};
64+
error('compareVersions:Error:VersionRef', sprintf('%s\n',msg{:}));
65+
end
66+
res = false(1, res_n);
67+
for i = 1:res_n
68+
shortVer = strsplit(verArray{i}, '.');
69+
shortVer = cellfun(@(x) str2double(regexp(x,'\d*','Match')), shortVer(1,:));
70+
longVer = strsplit(verComp, '.');
71+
longVer = cellfun(@(x) str2double(regexp(x,'\d*','Match')), longVer(1,:));
72+
shortVer_p = true;
73+
longVer_p = false;
74+
shortVer_s = length(shortVer);
75+
longVer_s = length(longVer);
76+
77+
if shortVer_s > longVer_s
78+
[longVer shortVer] = deal(shortVer,longVer);
79+
[longVer_s shortVer_s] = deal(shortVer_s,longVer_s);
80+
[longVer_p shortVer_p] = deal(shortVer_p,longVer_p);
81+
end
82+
83+
shortVer = [shortVer zeros(1,longVer_s - shortVer_s)];
84+
diff = shortVer - longVer;
85+
match = diff ~= 0;
86+
87+
if ~match
88+
res(i) = true;
89+
else
90+
pos = 1:longVer_s;
91+
pos = pos(match);
92+
val = diff(pos(1));
93+
if val > 0
94+
res(i) = shortVer_p;
95+
elseif val < 0
96+
res(i) = longVer_p;
97+
end
98+
end
99+
end
100+
end

0 commit comments

Comments
 (0)