<commit_before>"""
This module hosts functions to convert DataJoint table definitions into mysql table definitions, and to
declare the corresponding mysql tables.
"""
import re
import pyparsing as pp
import logging

from . import DataJointError

logger = logging.getLogger(__name__)


def declare(full_table_name, definition, context):
    """
    Parse declaration and create new SQL table accordingly.

    :param full_table_name: full name of the table
    :param definition: DataJoint table definition
    :param context: dictionary of objects that might be referred to in the table. Usually this will be locals()
    """
    # split definition into lines
    definition = re.split(r'\s*\n\s*', definition.strip())

    # check for optional table comment
    table_comment = definition.pop(0)[1:].strip() if definition[0].startswith('#') else ''

    in_key = True  # parse primary keys
    primary_key = []
    attributes = []
    attribute_sql = []
    foreign_key_sql = []
    index_sql = []

    for line in definition:
        if line.startswith('#'):  # additional comments are ignored
            pass
        elif line.startswith('---') or line.startswith('___'):
            in_key = False  # start parsing dependent attributes
        elif line.startswith('->'):
            # foreign key
            ref = eval(line[2:], context)()  # TODO: surround this with try...except... to give a better error message
            foreign_key_sql.append(
                'FOREIGN KEY ({primary_key})'
                ' REFERENCES {ref} ({primary_key})'
                ' ON UPDATE CASCADE ON DELETE RESTRICT'.format(
                    primary_key='`' + '`,`'.join(ref.primary_key) + '`', ref=ref.full_table_name)
            )
            for name in ref.primary_key:<commit_msg>Add detailed error messages in declare<commit_after>"""
This module hosts functions to convert DataJoint table definitions into mysql table definitions, and to
declare the corresponding mysql tables.
"""
import re
import pyparsing as pp
import logging

from . import DataJointError

logger = logging.getLogger(__name__)


def declare(full_table_name, definition, context):
    """
    Parse declaration and create new SQL table accordingly.

    :param full_table_name: full name of the table
    :param definition: DataJoint table definition
    :param context: dictionary of objects that might be referred to in the table. Usually this will be locals()
    """
    # split definition into lines
    definition = re.split(r'\s*\n\s*', definition.strip())

    # check for optional table comment
    table_comment = definition.pop(0)[1:].strip() if definition[0].startswith('#') else ''

    in_key = True  # parse primary keys
    primary_key = []
    attributes = []
    attribute_sql = []
    foreign_key_sql = []
    index_sql = []

    for line in definition:
        if line.startswith('#'):  # additional comments are ignored
            pass
        elif line.startswith('---') or line.startswith('___'):
            in_key = False  # start parsing dependent attributes
        elif line.startswith('->'):
            # foreign
            # TODO: clean up import order
            from .base_relation import BaseRelation
            # TODO: break this step into finer steps, checking the type of reference before calling it
            try:
                ref = eval(line[2:], context)()
            except NameError:
                raise DataJointError('Foreign key reference %s could not be resolved' % line[2:])
            except TypeError:
                raise DataJointError('Foreign key reference %s could not be instantiated.'
                                     'Make sure %s is a valid BaseRelation subclass' % line[2:])
            # TODO: consider the case where line[2:] is a function that returns an instance of BaseRelation
            if not isinstance(ref, BaseRelation):
                    raise DataJointError('Foreign key reference %s must be a subclass of BaseRelation' % line[2:])

            foreign_key_sql.append(
                'FOREIGN KEY ({primary_key})'
                ' REFERENCES {ref} ({primary_key})'
                ' ON UPDATE CASCADE ON DELETE RESTRICT'.format(
                    primary_key='`' + '`,`'.join(ref.primary_key) + '`', ref=ref.full_table_name)
            )
            for name in ref.primary_key:
{'fraction_non_alphanumeric': 0.09535845588235294, 'fraction_numerical': 0.0027573529411764708, 'mean_word_length': 2.5104838709677417, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6396462', 'n_tokens_mistral': 1162, 'n_tokens_neox': 1109, 'n_words': 404}