@@ -114,12 +114,12 @@ def build_foreign_key_parser():
114114 return arrow + options + ref_table
115115
116116
117- def build_attribute_parser ():
117+ def build_attribute_parser (parse_metadata = False ):
118118 quoted = pp .QuotedString ('"' ) ^ pp .QuotedString ("'" )
119119 colon = pp .Literal (":" ).suppress ()
120- attribute_name = pp .Word (pp . srange ( "[a-z]" ), pp . srange ( "[a-z0-9_]" )). setResultsName (
121- "name"
122- )
120+ attribute_name = pp .Word (
121+ pp . srange ( f"[a-z { '_' if parse_metadata else '' } ]" ), pp . srange ( "[a-z0-9_]" )
122+ ). setResultsName ( "name" )
123123 data_type = (
124124 pp .Combine (pp .Word (pp .alphas ) + pp .SkipTo ("#" , ignore = quoted ))
125125 ^ pp .QuotedString ("<" , endQuoteChar = ">" , unquoteResults = False )
@@ -134,6 +134,7 @@ def build_attribute_parser():
134134foreign_key_parser_old = build_foreign_key_parser_old ()
135135foreign_key_parser = build_foreign_key_parser ()
136136attribute_parser = build_attribute_parser ()
137+ metadata_attribute_parser = build_attribute_parser (parse_metadata = True )
137138
138139
139140def is_foreign_key (line ):
@@ -245,6 +246,7 @@ def prepare_declare(definition, context):
245246 foreign_key_sql = []
246247 index_sql = []
247248 external_stores = []
249+ metadata_attributes = ["_timestamp = CURRENT_TIMESTAMP : timestamp" ]
248250
249251 for line in definition :
250252 if not line or line .startswith ("#" ): # ignore additional comments
@@ -272,6 +274,12 @@ def prepare_declare(definition, context):
272274 if name not in attributes :
273275 attributes .append (name )
274276 attribute_sql .append (sql )
277+ for line in metadata_attributes :
278+ name , sql , store = compile_attribute (
279+ line , in_key , foreign_key_sql , context , is_metadata = True
280+ )
281+ attributes .append (name )
282+ attribute_sql .append (sql )
275283
276284 return (
277285 table_comment ,
@@ -496,18 +504,22 @@ def substitute_special_type(match, category, foreign_key_sql, context):
496504 assert False , "Unknown special type"
497505
498506
499- def compile_attribute (line , in_key , foreign_key_sql , context ):
507+ def compile_attribute (line , in_key , foreign_key_sql , context , is_metadata = False ):
500508 """
501509 Convert attribute definition from DataJoint format to SQL
502510
503511 :param line: attribution line
504512 :param in_key: set to True if attribute is in primary key set
505513 :param foreign_key_sql: the list of foreign key declarations to add to
506514 :param context: context in which to look up user-defined attribute type adapterss
515+ :param is_metadata: flag to use an alternate parser for metadata attributes
507516 :returns: (name, sql, is_external) -- attribute name and sql code for its declaration
508517 """
509518 try :
510- match = attribute_parser .parseString (line + "#" , parseAll = True )
519+ if is_metadata :
520+ match = metadata_attribute_parser .parseString (line + "#" , parseAll = True )
521+ else :
522+ match = attribute_parser .parseString (line + "#" , parseAll = True )
511523 except pp .ParseException as err :
512524 raise DataJointError (
513525 "Declaration error in position {pos} in line:\n {line}\n {msg}" .format (
0 commit comments