**Use case**

I want to estimate better parameters for fulltext (`ngrambf_v1`) index.

**Describe the solution you'd like**

Implement it as SQL function:

`ngrams(string, N)`

that returns array of FixedString.

**Describe alternatives you've considered**

It can be done with less efficient and more cumbersome way:

```
WITH 3 AS n, extractAll(text, '.') AS chars 
SELECT 
  arrayMap(x -> arrayStringConcat(x), 
    arrayFilter(x -> length(x) = n, 
      arrayMap(x, i -> arraySlice(chars, i, n), 
        chars, arrayEnumerate(chars)))) AS ngrams
```

**Additional context**

`ngramsUTF8` can also be implemented to extract ngrams of Unicode code points.
const char * getFamilyName() const override { return "FixedString"; }
    size_t getN() const
    {
        return n;
    }
    MutableColumnPtr createColumn() const override;
    Field getDefault() const override;
    bool equals(const IDataType & rhs) const override;
    SerializationPtr doGetDefaultSerialization() const override;
    bool isParametric() const override { return true; }
    bool haveSubtypes() const override { return false; }
    bool isComparable() const override { return true; }
    bool isValueUnambiguouslyRepresentedInContiguousMemoryRegion() const override { return true; }
    bool isValueUnambiguouslyRepresentedInFixedSizeContiguousMemoryRegion() const override { return true; }
    bool haveMaximumSizeOfValue() const override { return true; }
    size_t getSizeOfValueInMemory() const override { return n; }
    bool isCategorial() const override { return true; }
    bool canBeInsideNullable() const override { return true; }
    bool canBeInsideLowCardinality() const override { return true; }
    
    
    
    void alignStringLength(PaddedPODArray<UInt8> & chars, size_t old_size) const;
};
}