@@ -135,12 +135,14 @@ case strncmp(line,'---',3)
135135 inKey = false ;
136136 % foreign key
137137 case regexp(line , ' ^(\s*\([^)]+\)\s*)?->.+$' )
138- [fk_attr_sql , fk_sql , newFields ] = dj .internal .Declare .makeFK( ...
139- line , fields , inKey , ...
140- dj .internal .shorthash(sprintf(' `%s `.`%s `' , ...
141- table_instance .schema .dbname , tableName )));
138+ [fk_attr_sql , fk_sql , newFields , idx_sql ] = ...
139+ dj .internal .Declare .makeFK( ...
140+ line , fields , inKey , ...
141+ dj .internal .shorthash(sprintf(' `%s `.`%s `' , ...
142+ table_instance .schema .dbname , tableName )));
142143 attributeSql = [attributeSql , fk_attr_sql ]; % #ok<AGROW>
143144 foreignKeySql = [foreignKeySql , fk_sql ]; % #ok<AGROW>
145+ indexSql = [indexSql , idx_sql ]; % #ok<AGROW>
144146 fields = [fields , newFields ]; % #ok<AGROW>
145147 if inKey
146148 primaryFields = [primaryFields , newFields ]; % #ok<AGROW>
@@ -236,7 +238,8 @@ case regexp(line, ['^[a-z][a-z\d_]*\s*' ... % name
236238 fieldInfo.isnullable = strcmpi(fieldInfo .default ,' null' );
237239 end
238240
239- function [all_attr_sql , fk_sql , newattrs ] = makeFK(line , existingFields , inKey , hash )
241+ function [all_attr_sql , fk_sql , newattrs , idx_sql ] = makeFK(line , existingFields , ...
242+ inKey , hash )
240243 % [sql, newattrs] = MAKEFK(sql, line, existingFields, inKey, hash)
241244 % Add foreign key to SQL table definition.
242245 % sql: <string> Modified in-place SQL to include foreign keys.
@@ -247,8 +250,11 @@ case regexp(line, ['^[a-z][a-z\d_]*\s*' ... % name
247250 % hash: <string> Current hash as base.
248251 fk_sql = ' ' ;
249252 all_attr_sql = ' ' ;
253+ idx_sql = ' ' ;
250254 pat = [' ^(?<newattrs>\([\s\w,]*\))?' ...
251255 ' \s*->\s*' ...
256+ ' (?<options>\[[\s\w,]*\])?' ...
257+ ' \s*' ...
252258 ' (?<cname>\w+\.[A-Z][A-Za-z0-9]*)' ...
253259 ' \w*' ...
254260 ' (?<attrs>\([\s\w,]*\))?' ...
@@ -263,12 +269,17 @@ case regexp(line, ['^[a-z][a-z\d_]*\s*' ... % name
263269
264270 % parse and validate the attribute lists
265271 attrs = strsplit(fk .attrs , {' ' ,' ,' ,' (' ,' )' });
272+ options = strsplit(fk .options , {' ' ,' ,' ,' [' ,' ]' });
266273 newattrs = strsplit(fk .newattrs , {' ' ,' ,' ,' (' ,' )' });
267274 attrs(cellfun(@isempty , attrs ))=[];
275+ options(cellfun(@isempty , options ))=[];
268276 newattrs(cellfun(@isempty , newattrs ))=[];
269277 assert(all(cellfun(@(a ) ismember(a , rel .primaryKey ), attrs )), ...
270278 ' All attributes in (%s ) must be in the primary key of %s ' , ...
271279 strjoin(attrs , ' ,' ), rel .className )
280+ assert(~inKey || ~any(strcmpi(' NULLABLE' , options )), ...
281+ sprintf([' Primary dependencies cannot be ' ...
282+ ' nullable in line "%s "' ], line ));
272283 if length(newattrs )==1
273284 % unambiguous single attribute
274285 if length(rel .primaryKey )==1
@@ -298,7 +309,7 @@ case regexp(line, ['^[a-z][a-z\d_]*\s*' ... % name
298309 fieldInfo = rel .tableHeader .attributes(strcmp(attrs{i }, ...
299310 rel .tableHeader .names ));
300311 fieldInfo.name = newattrs{i };
301- fieldInfo.nullabe = ~inKey ; % nonprimary references are nullable
312+ fieldInfo.isnullable = logical( ~inKey * any(strcmpi( ' NULLABLE ' , options )));
302313 [attr_sql , ~ , ~ ] = dj .internal .Declare .compileAttribute(fieldInfo , []);
303314 all_attr_sql = sprintf(' %s%s ,\n ' , all_attr_sql , attr_sql );
304315 end
@@ -311,6 +322,9 @@ case regexp(line, ['^[a-z][a-z\d_]*\s*' ... % name
311322 [' %s CONSTRAINT `%s ` FOREIGN KEY (%s ) REFERENCES %s (%s ) ' ...
312323 ' ON UPDATE CASCADE ON DELETE RESTRICT' ], fk_sql , hash , ...
313324 backquotedList(fkattrs ), rel .fullTableName , backquotedList(rel .primaryKey ));
325+ if any(strcmpi(' UNIQUE' , options ))
326+ idx_sql = sprintf(' UNIQUE INDEX (%s )' , [' `' strjoin(newattrs , ' `,`' ) ' `' ]);
327+ end
314328 end
315329
316330 function [field , foreignKeySql ] = substituteSpecialType(field , category , foreignKeySql )
0 commit comments