docs: def copy_to(self, file=None, table_or_view=None, sep='\t', columns=None,
                  check_constraints=False, fire_triggers=False, keep_nulls=False,
                  kb_per_batch=None, rows_per_batch=None, order=None, tablock=False,
                  schema=None, null_string=None, data=None):
          """ *Experimental*. Efficiently load data to database from file using ``BULK INSERT`` operation
  
          :param file: Source file-like object, should be in csv format. Specify
            either this or data, not both.
          :param table_or_view: Destination table or view in the database
          :type table_or_view: str
  
          Optional parameters:
  
          :keyword sep: Separator used in csv file
          :type sep: str
          :keyword columns: List of Column objects or column names in target
            table to insert to. SQL Server will do some conversions, so these
            may not have to match the actual table definition exactly.
            If not provided will insert into all columns assuming nvarchar(4000)
            NULL for all columns.
            If only the column name is provided, the type is assumed to be
            nvarchar(4000) NULL.
            If rows are given with file, you cannot specify non-string data
            types.
            If rows are given with data, the values must be a type supported by
            the serializer for the column in tds_types.
          :type columns: list
          :keyword check_constraints: Check table constraints for incoming data
          :type check_constraints: bool
          :keyword fire_triggers: Enable or disable triggers for table
          :type fire_triggers: bool
          :keyword keep_nulls: If enabled null values inserted as-is, instead of
            inserting default value for column
          :type keep_nulls: bool
          :keyword kb_per_batch: Kilobytes per batch can be used to optimize performance, see MSSQL
            server documentation for details
          :type kb_per_batch: int
          :keyword rows_per_batch: Rows per batch can be used to optimize performance, see MSSQL
            server documentation for details
          :type rows_per_batch: int
          :keyword order: The ordering of the data in source table. List of columns with ASC or DESC suffix.
            E.g. ``['order_id ASC', 'name DESC']``
            Can be used to optimize performance, see MSSQL server documentation for details
          :type order: list
          :keyword tablock: Enable or disable table lock for the duration of bulk load
          :keyword schema: Name of schema for table or view, if not specified default schema will be used
          :keyword null_string: String that should be interpreted as a NULL when
            reading the CSV file. Has no meaning if using data instead of file.
          :keyword data: The data to insert as an iterable of rows, which are
            iterables of values. Specify either this or file, not both.
          """
          conn = self._conn()
          rows = None
          if data is None:
              import csv
              reader = csv.reader(file, delimiter=sep)
  
              if null_string is not None:
                  def _convert_null_strings(csv_reader):
                      for row in csv_reader:
                          yield [r if r != null_string else None for r in row]
  
                  reader = _convert_null_strings(reader)
  
              rows = reader
          else:
              rows = data
  
          obj_name = tds_base.tds_quote_id(table_or_view)
          if schema:
              obj_name = '{0}.{1}'.format(tds_base.tds_quote_id(schema), obj_name)
          if columns:
              metadata = []
              for column in columns:
                  if isinstance(column, Column):
                      metadata.append(column)
                  else:
                      metadata.append(Column(name=column, type=NVarCharType(size=4000), flags=Column.fNullable))
          else:
              self.execute('select top 1 * from {} where 1<>1'.format(obj_name))
              metadata = [Column(name=col[0], type=NVarCharType(size=4000), flags=Column.fNullable if col[6] else 0)
                          for col in self.description]
          col_defs = ','.join('{0} {1}'.format(tds_base.tds_quote_id(col.column_name), col.type.get_declaration())
                              for col in metadata)
          with_opts = []
          if check_constraints:
              with_opts.append('CHECK_CONSTRAINTS')
          if fire_triggers:
              with_opts.append('FIRE_TRIGGERS')
          if keep_nulls:
              with_opts.append('KEEP_NULLS')
          if kb_per_batch:
              with_opts.append('KILOBYTES_PER_BATCH = {0}'.format(kb_per_batch))
          if rows_per_batch:
              with_opts.append('ROWS_PER_BATCH = {0}'.format(rows_per_batch))
          if order:
              with_opts.append('ORDER({0})'.format(','.join(order)))
          if tablock:
              with_opts.append('TABLOCK')
          with_part = ''
          if with_opts:
              with_part = 'WITH ({0})'.format(','.join(with_opts))
          operation = 'INSERT BULK {0}({1}) {2}'.format(obj_name, col_defs, with_part)
          self.execute(operation)
          self._session.submit_bulk(metadata, rows)
          self._session.process_simple_request()