docs: def init_from_database(self, conn, database, table_name):
          """
          initialize heading from a database table.  The table must exist already.
          """
          info = conn.query('SHOW TABLE STATUS FROM `{database}` WHERE name="{table_name}"'.format(
              table_name=table_name, database=database), as_dict=True).fetchone()
          if info is None:
              if table_name == '~log':
                  logger.warning('Could not create the ~log table')
                  return
              else:
                  raise DataJointError('The table `{database}`.`{table_name}` is not defined.'.format(
                      table_name=table_name, database=database))
          self.table_info = {k.lower(): v for k, v in info.items()}
  
          cur = conn.query(
              'SHOW FULL COLUMNS FROM `{table_name}` IN `{database}`'.format(
                  table_name=table_name, database=database), as_dict=True)
  
          attributes = cur.fetchall()
  
          rename_map = {
              'Field': 'name',
              'Type': 'type',
              'Null': 'nullable',
              'Default': 'default',
              'Key': 'in_key',
              'Comment': 'comment'}
  
          fields_to_drop = ('Privileges', 'Collation')
  
          # rename and drop attributes
          attributes = [{rename_map[k] if k in rename_map else k: v
                         for k, v in x.items() if k not in fields_to_drop}
                        for x in attributes]
  
          numeric_types = {
              ('float', False): np.float64,
              ('float', True): np.float64,
              ('double', False): np.float64,
              ('double', True): np.float64,
              ('tinyint', False): np.int64,
              ('tinyint', True): np.int64,
              ('smallint', False): np.int64,
              ('smallint', True): np.int64,
              ('mediumint', False): np.int64,
              ('mediumint', True): np.int64,
              ('int', False): np.int64,
              ('int', True): np.int64,
              ('bigint', False): np.int64,
              ('bigint', True): np.uint64}
  
          sql_literals = ['CURRENT_TIMESTAMP']
  
          # additional attribute properties
          for attr in attributes:
              # process external attributes
              split_comment = attr['comment'].split(':')
              attr['is_external'] = len(split_comment) >= 3 and split_comment[1].startswith('external')
              if attr['is_external']:
                  attr['comment'] = ':'.join(split_comment[2:])
                  attr['type'] = split_comment[1]
  
              attr['nullable'] = (attr['nullable'] == 'YES')
              attr['in_key'] = (attr['in_key'] == 'PRI')
              attr['autoincrement'] = bool(re.search(r'auto_increment', attr['Extra'], flags=re.IGNORECASE))
              attr['type'] = re.sub(r'int\(\d+\)', 'int', attr['type'], count=1)   # strip size off integers
              attr['numeric'] = bool(re.match(r'(tiny|small|medium|big)?int|decimal|double|float', attr['type']))
              attr['string'] = bool(re.match(r'(var)?char|enum|date|year|time|timestamp', attr['type']))
              attr['is_blob'] = attr['is_external'] or bool(re.match(r'(tiny|medium|long)?blob', attr['type']))
              attr['database'] = database
  
              if attr['string'] and attr['default'] is not None and attr['default'] not in sql_literals:
                  attr['default'] = '"%s"' % attr['default']
  
              if attr['nullable']:   # nullable fields always default to null
                  attr['default'] = 'null'
  
              attr['sql_expression'] = None
              if not (attr['numeric'] or attr['string'] or attr['is_blob']):
                  raise DataJointError('Unsupported field type {field} in `{database}`.`{table_name}`'.format(
                      field=attr['type'], database=database, table_name=table_name))
              attr.pop('Extra')
  
              # fill out dtype. All floats and non-nullable integers are turned into specific dtypes
              attr['dtype'] = object
              if attr['numeric']:
                  is_integer = bool(re.match(r'(tiny|small|medium|big)?int', attr['type']))
                  is_float = bool(re.match(r'(double|float)', attr['type']))
                  if is_integer and not attr['nullable'] or is_float:
                      is_unsigned = bool(re.match('\sunsigned', attr['type'], flags=re.IGNORECASE))
                      t = attr['type']
                      t = re.sub(r'\(.*\)', '', t)    # remove parentheses
                      t = re.sub(r' unsigned$', '', t)   # remove unsigned
                      assert (t, is_unsigned) in numeric_types, 'dtype not found for type %s' % t
                      attr['dtype'] = numeric_types[(t, is_unsigned)]
          self.attributes = OrderedDict([(q['name'], Attribute(**q)) for q in attributes])
  
          # Read and tabulate secondary indexes
          keys = defaultdict(dict)
          for item in conn.query('SHOW KEYS FROM `{db}`.`{tab}`'.format(db=database, tab=table_name), as_dict=True):
              if item['Key_name'] != 'PRIMARY':
                  keys[item['Key_name']][item['Seq_in_index']] = dict(
                      column=item['Column_name'],
                      unique=(item['Non_unique'] == 0),
                      nullable=item['Null'].lower() == 'yes')
          self.indexes = {
              tuple(item[k]['column'] for k in sorted(item.keys())):
                  dict(unique=item[1]['unique'],
                       nullable=any(v['nullable'] for v in item.values()))
              for item in keys.values()}