docs: def insert_fake_data(hosts=None,
                       table=None,
                       num_records=1e5,
                       bulk_size=1000,
                       concurrency=25,
                       mapping_file=None):
      """Generate random data and insert it into a table.
  
      This will read the table schema and then find suitable random data providers.
      Which provider is choosen depends on the column name and data type.
  
      Example:
  
          A column named `name` will map to the `name` provider.
          A column named `x` of type int will map to `random_int` because there
          is no `x` provider.
  
      Available providers are listed here:
          https://faker.readthedocs.io/en/latest/providers.html
  
          Additional providers:
          - auto_inc:
              Returns unique incrementing numbers.
              Automatically used for columns named "id" of type int or long
          - geo_point
              Returns [<lon>, <lat>]
              Automatically used for columns of type geo_point
  
      Args:
          hosts: <host>:[<port>] of the Crate node
          table: The table name into which the data should be inserted.
              Either fully qualified: `<schema>.<table>` or just `<table>`
          num_records: Number of records to insert.
              Usually a number but expressions like `1e4` work as well.
          bulk_size: The bulk size of the insert statements.
          concurrency: How many operations to run concurrently.
          mapping_file: A JSON file that defines a mapping from column name to
              fake-factory provider.
              The format is as follows:
              {
                  "column_name": ["provider_with_args", ["arg1", "arg"]],
                  "x": ["provider_with_args", ["arg1"]],
                  "y": "provider_without_args"
              }
      """
      with clients.client(hosts, concurrency=1) as client:
          schema, table_name = parse_table(table)
          columns = retrieve_columns(client, schema, table_name)
      if not columns:
          sys.exit('Could not find columns for table "{}"'.format(table))
      print('Found schema: ')
      print(json.dumps(columns, sort_keys=True, indent=4))
      mapping = None
      if mapping_file:
          mapping = json.load(mapping_file)
  
      bulk_size = min(num_records, bulk_size)
      num_inserts = int(math.ceil(num_records / bulk_size))
  
      gen_row = create_row_generator(columns, mapping)
  
      stmt = to_insert('"{schema}"."{table_name}"'.format(**locals()), columns)[0]
      print('Using insert statement: ')
      print(stmt)
  
      print('Will make {} requests with a bulk size of {}'.format(
          num_inserts, bulk_size))
  
      print('Generating fake data and executing inserts')
      q = asyncio.Queue(maxsize=concurrency)
      with clients.client(hosts, concurrency=concurrency) as client:
          active = [True]
  
          def stop():
              asyncio.ensure_future(q.put(None))
              active.clear()
              loop.remove_signal_handler(signal.SIGINT)
          if sys.platform != 'win32':
              loop.add_signal_handler(signal.SIGINT, stop)
          bulk_seq = _bulk_size_generator(num_records, bulk_size, active)
          with ThreadPoolExecutor() as e:
              tasks = asyncio.gather(
                  _gen_data_and_insert(q, e, client, stmt, gen_row, bulk_seq),
                  consume(q, total=num_inserts)
              )
              loop.run_until_complete(tasks)