using System.Runtime.CompilerServices;
using NpgsqlTypes;
namespace NpgsqlRest;
///
/// Flags enum representing PostgreSQL type categories for optimized type dispatch.
/// Used for fast bitwise checks instead of multiple boolean property accesses.
///
[Flags]
public enum TypeCategory : ushort
{
None = 0,
/// Smallint, Integer, Bigint, Numeric, Real, Double, Money
Numeric = 1 << 0,
/// Boolean
Boolean = 1 << 1,
/// Json, Jsonb
Json = 1 << 2,
/// Text, Varchar, Char, Name, Xml, JsonPath
Text = 1 << 3,
/// Timestamp, TimestampTz
DateTime = 1 << 4,
/// Date
Date = 1 << 5,
/// Types requiring JSON string escaping
NeedsEscape = 1 << 6,
/// Types requiring ::text cast
CastToText = 1 << 7,
/// Bytea
Binary = 1 << 8,
/// Time, TimeTz
Time = 1 << 9,
}
///
/// Pre-computed lookup table for NpgsqlDbType to TypeCategory mapping.
/// Enables O(1) type category lookup instead of multiple switch expressions.
///
public static class TypeCategoryLookup
{
private static readonly TypeCategory[] _lookup;
static TypeCategoryLookup()
{
// NpgsqlDbType values range from 0 to approximately 70+
// Use 128 as safe upper bound
_lookup = new TypeCategory[128];
// Numeric types
_lookup[(int)NpgsqlDbType.Smallint] = TypeCategory.Numeric;
_lookup[(int)NpgsqlDbType.Integer] = TypeCategory.Numeric;
_lookup[(int)NpgsqlDbType.Bigint] = TypeCategory.Numeric;
_lookup[(int)NpgsqlDbType.Numeric] = TypeCategory.Numeric;
_lookup[(int)NpgsqlDbType.Real] = TypeCategory.Numeric;
_lookup[(int)NpgsqlDbType.Double] = TypeCategory.Numeric;
_lookup[(int)NpgsqlDbType.Money] = TypeCategory.Numeric;
// Boolean
_lookup[(int)NpgsqlDbType.Boolean] = TypeCategory.Boolean;
// JSON types
_lookup[(int)NpgsqlDbType.Json] = TypeCategory.Json | TypeCategory.Text;
_lookup[(int)NpgsqlDbType.Jsonb] = TypeCategory.Json | TypeCategory.Text;
// Text types (also set NeedsEscape for those requiring JSON escaping)
_lookup[(int)NpgsqlDbType.Text] = TypeCategory.Text | TypeCategory.NeedsEscape;
_lookup[(int)NpgsqlDbType.Varchar] = TypeCategory.Text | TypeCategory.NeedsEscape;
_lookup[(int)NpgsqlDbType.Char] = TypeCategory.Text | TypeCategory.NeedsEscape;
_lookup[(int)NpgsqlDbType.Name] = TypeCategory.Text | TypeCategory.NeedsEscape;
_lookup[(int)NpgsqlDbType.Xml] = TypeCategory.Text | TypeCategory.NeedsEscape;
_lookup[(int)NpgsqlDbType.JsonPath] = TypeCategory.Text | TypeCategory.NeedsEscape;
// DateTime types
_lookup[(int)NpgsqlDbType.Timestamp] = TypeCategory.DateTime;
_lookup[(int)NpgsqlDbType.TimestampTz] = TypeCategory.DateTime;
// Date
_lookup[(int)NpgsqlDbType.Date] = TypeCategory.Date;
// Time types
_lookup[(int)NpgsqlDbType.Time] = TypeCategory.Time;
_lookup[(int)NpgsqlDbType.TimeTz] = TypeCategory.Time;
// Binary
_lookup[(int)NpgsqlDbType.Bytea] = TypeCategory.Binary | TypeCategory.NeedsEscape | TypeCategory.CastToText;
// Types that need escape (non-text types that still need JSON escaping)
_lookup[(int)NpgsqlDbType.TsQuery] = TypeCategory.NeedsEscape | TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.TsVector] = TypeCategory.NeedsEscape | TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Citext] = TypeCategory.NeedsEscape | TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.LQuery] = TypeCategory.NeedsEscape | TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.LTree] = TypeCategory.NeedsEscape | TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.LTxtQuery] = TypeCategory.NeedsEscape | TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Hstore] = TypeCategory.NeedsEscape | TypeCategory.CastToText;
// Types that need cast to text (but don't necessarily need escape)
_lookup[(int)NpgsqlDbType.Interval] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Bit] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Varbit] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Inet] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.MacAddr] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Cidr] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.MacAddr8] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Box] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Circle] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Line] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.LSeg] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Path] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Point] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Polygon] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Oid] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Xid] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Xid8] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Cid] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Regtype] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Regconfig] = TypeCategory.CastToText;
// Note: Range and Multirange types (IntegerRange, BigIntRange, etc.) are composed values
// (e.g., Range | Integer = 0x40000000 | 9) and cannot be stored in a simple lookup.
// They are handled separately - the GetCategory method returns CastToText for them
// via the IsRangeOrMultirange check.
_lookup[(int)NpgsqlDbType.Int2Vector] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Oidvector] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.PgLsn] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Tid] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Geometry] = TypeCategory.CastToText;
_lookup[(int)NpgsqlDbType.Geography] = TypeCategory.CastToText;
// UUID and Refcursor don't have specific categories - they're handled as-is
_lookup[(int)NpgsqlDbType.Uuid] = TypeCategory.None;
_lookup[(int)NpgsqlDbType.Refcursor] = TypeCategory.Text;
}
// NpgsqlDbType flags for Range and Multirange
private const int RangeFlag = 0x40000000; // NpgsqlDbType.Range
private const int MultirangeFlag = 0x20000000; // NpgsqlDbType.Multirange
///
/// Gets the type category for the specified NpgsqlDbType.
/// Array, Range, and Multirange modifiers are automatically stripped before lookup.
/// Range and Multirange types are automatically classified as CastToText.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TypeCategory GetCategory(NpgsqlDbType dbType)
{
int rawValue = (int)dbType;
// Check for Range or Multirange types - these always need CastToText
if ((rawValue & (RangeFlag | MultirangeFlag)) != 0)
{
return TypeCategory.CastToText;
}
// Strip Array flag and look up in table
int index = rawValue & ~(int)NpgsqlDbType.Array;
return (uint)index < (uint)_lookup.Length ? _lookup[index] : TypeCategory.None;
}
}