HexaBridge - SQL Server (T-SQL) Compatibility for PostgreSQL
HexaBridge brings SQL Server's T-SQL functions, data types, and operators to Community PostgreSQL - no proprietary fork required. 100% routine compatibility, delivered at native speed through C extensions.
Deep T-SQL Compatibility on Community PostgreSQL
Moving from SQL Server to PostgreSQL usually means rewriting every T-SQL function call, every DATEADD/DATEDIFF, every CHARINDEX, and re-implementing types like nvarchar, uniqueidentifier, and hierarchyid by hand. HexaBridge removes that work: it resolves SQL Server functions and type names natively in PostgreSQL's sys schema, with SQL Server semantics (boundary-crossing DATEDIFF, trailing-space-insensitive nvarchar comparison, LEN trimming, T-SQL + string concatenation).
HexaBridge covers 100% of the SQL Server routine surface while preserving SQL Server semantics. Logic-heavy functions are written in C and direct maps are inlined by the planner, so the compatibility layer adds no performance overhead. It runs on Community PostgreSQL as-is with a single CREATE EXTENSION. No proprietary database fork required.
The compatibility no AI or migration tool can generate
AI and migration tools convert your schema and application code, but they cannot recreate the source database's built-in routines on PostgreSQL. HexaBridge provides exactly that runtime compatibility, so HexaRocket can achieve 100% automatic schema conversion. Without this layer, every built-in function your application relies on becomes a manual rewrite that no tool can fully automate.
Works on Community PostgreSQL - No Proprietary Fork Required
HexaBridge for SQL Server is a standard PostgreSQL extension that installs with a simple CREATE EXTENSION hexabridge_mssql; command.
It is available to customers who migrate with HexaRocket and to customers of HexaCluster-approved partners. Contact us to see how it accelerates your SQL Server-to-PostgreSQL migration.
SQL Server-Compatible Types
12 types including nvarchar, nchar, datetime2, uniqueidentifier, rowversion, smallmoney, sql_variant, and a full hierarchyid. Optional geometry/geography via PostGIS.
SQL Server-Compatible Functions
180+ functions and aggregates - DATEADD, DATEDIFF, CHARINDEX, ISNULL, FORMAT, TRY_CONVERT, COMPRESS/DECOMPRESS, OPENJSON, and more.
Native Performance
Advanced performance through native C extensions. Logic-heavy functions run in C and direct maps are inlined by the planner, so the compatibility layer adds no performance overhead.
How It Works
-- Install HexaBridge for SQL Server on Community PostgreSQL
CREATE EXTENSION hexabridge_mssql;
-- Resolve sys.* / dbo.* names like SQL Server
SET search_path TO sys, public;
-- Use T-SQL functions naturally
SELECT charindex('cd', 'abcdef', 2); -- 3
SELECT dateadd('month', 2, '2021-01-31'); -- 2021-03-31 00:00:00
SELECT datediff('year', '2020-12-31', '2021-01-01'); -- 1 (boundary crossing)
SELECT isnull(NULL, 'default'); -- default
-- Optional: geometry/geography (requires PostGIS)
-- CREATE EXTENSION hexabridge_mssql_spatial CASCADE;SQL Server-Compatible Data Types
HexaBridge provides 12 SQL Server data types that behave like their T-SQL counterparts - character-count length semantics, trailing-space-insensitive comparison, and SQL Server datetime precision. Optional geometry/geography are available via a separate PostGIS-backed submodule.
All types are created in the sys schema. With SET search_path TO sys, public;, they can be used by bare name - just like in SQL Server.
Optional spatial types geometry and geography map 1:1 onto PostGIS via the separate hexabridge_mssql_spatial submodule, which requires PostGIS. The core extension has no PostGIS dependency.
SQL Server-Compatible Functions
HexaBridge provides 180+ standalone T-SQL functions and aggregates that follow SQL Server semantics - boundary-crossing DATEDIFF, char-accurate CHARINDEX, LEN trailing-space trimming, ISNULL, .NET-style FORMAT, and more. Analytic window functions (ROW_NUMBER, RANK, DENSE_RANK, NTILE, LEAD, LAG, FIRST_VALUE, LAST_VALUE, CUME_DIST, PERCENT_RANK) are native PostgreSQL and work unchanged.
String / Character(15)
charindex(needle, haystack [, start])1-based, char-accurate (multibyte-safe) search with optional start; empty needle returns 0. C-implemented.charindex(needle, haystack [, start])1-based, char-accurate (multibyte-safe) search with optional start; empty needle returns 0. C-implemented.len(str)Character length excluding trailing spaces (SQL Server semantics).len(str)Character length excluding trailing spaces (SQL Server semantics).patindex(pattern, str)1-based position of a LIKE pattern (%/_).patindex(pattern, str)1-based position of a LIKE pattern (%/_).left(str, n) / right(str, n)Leftmost / rightmost n characters.left(str, n) / right(str, n)Leftmost / rightmost n characters.replicate(str, n)Repeats a string n times.replicate(str, n)Repeats a string n times.space(n)Returns n spaces.space(n)Returns n spaces.stuff(str, start, length, replace)Deletes length chars at start and inserts replace.stuff(str, start, length, replace)Deletes length chars at start and inserts replace.isnumeric(str)1 if the string is a valid numeric literal, else 0.isnumeric(str)1 if the string is a valid numeric literal, else 0.concat(v1, v2, ...)Concatenates, treating NULL as empty string.concat(v1, v2, ...)Concatenates, treating NULL as empty string.concat_ws(sep, v1, v2, ...)Concatenates with a separator, skipping NULLs.concat_ws(sep, v1, v2, ...)Concatenates with a separator, skipping NULLs.string_split(str, sep)Table-valued split into rows.string_split(str, sep)Table-valued split into rows.quotename(str [, quote_char])Delimits an identifier (default []).quotename(str [, quote_char])Delimits an identifier (default []).str(float [, length [, decimals]])Right-justified numeric-to-string; * overflow fill.str(float [, length [, decimals]])Right-justified numeric-to-string; * overflow fill.string_escape(str, 'json')JSON-escapes a string.string_escape(str, 'json')JSON-escapes a string.unicode(str)Unicode code point of the first character.unicode(str)Unicode code point of the first character.Date / Time(16)
datepart(part, date)Extracts a date part (year, month, day, hour, ..., dayofyear, week, weekday). C-implemented token parsing.datepart(part, date)Extracts a date part (year, month, day, hour, ..., dayofyear, week, weekday). C-implemented token parsing.dateadd(part, n, date)Adds n of a date part with SQL Server end-of-month snapping.dateadd(part, n, date)Adds n of a date part with SQL Server end-of-month snapping.datediff(part, start, end)Boundary-crossing count (not elapsed-unit subtraction). Overflow-checked.datediff(part, start, end)Boundary-crossing count (not elapsed-unit subtraction). Overflow-checked.datediff_big(part, start, end)Like DATEDIFF but returns bigint.datediff_big(part, start, end)Like DATEDIFF but returns bigint.datename(part, date)Textual name of a date part.datename(part, date)Textual name of a date part.year(date) / month(date) / day(date)Component extractors.year(date) / month(date) / day(date)Component extractors.eomonth(date [, months])Last day of the month (optionally offset).eomonth(date [, months])Last day of the month (optionally offset).getdate() / getutcdate()Current statement timestamp (local / UTC). STABLE.getdate() / getutcdate()Current statement timestamp (local / UTC). STABLE.sysdatetime() / sysutcdatetime() / sysdatetimeoffset()High-precision current timestamps.sysdatetime() / sysutcdatetime() / sysdatetimeoffset()High-precision current timestamps.datefromparts(y,m,d), datetimefromparts(...), smalldatetimefromparts(...), datetimeoffsetfromparts(...), timefromparts(...)Build datetime values from components.datefromparts(y,m,d), datetimefromparts(...), smalldatetimefromparts(...), datetimeoffsetfromparts(...), timefromparts(...)Build datetime values from components.datetrunc(part, date)Truncate to a date part.datetrunc(part, date)Truncate to a date part.date_bucket(part, n, date [, origin])Bucket a date into fixed-width bins.date_bucket(part, n, date [, origin])Bucket a date into fixed-width bins.isdate(str)1 if the string parses as a date, else 0.isdate(str)1 if the string parses as a date, else 0.switchoffset(dto, tz) / todatetimeoffset(dt, tz)Adjust/attach a timezone offset. (tz-display semantics differ; functions present.)switchoffset(dto, tz) / todatetimeoffset(dt, tz)Adjust/attach a timezone offset. (tz-display semantics differ; functions present.)format(value, format [, culture]).NET-style date formatting.format(value, format [, culture]).NET-style date formatting.datetime_round(ts)Exact SQL Server 1/300-second DATETIME tick rounding.datetime_round(ts)Exact SQL Server 1/300-second DATETIME tick rounding.Numeric / Math(10)
abs(n) / sign(n) / ceiling(n) / floor(n)Type-preserving overloads (int/bigint/numeric/float).abs(n) / sign(n) / ceiling(n) / floor(n)Type-preserving overloads (int/bigint/numeric/float).round(n, length [, function])Scale-preserving round; truncate flag (SQL Server semantics).round(n, length [, function])Scale-preserving round; truncate flag (SQL Server semantics).square(n)n * n.square(n)n * n.sqrt(n) / exp(n) / power(n, e)Standard math.sqrt(n) / exp(n) / power(n, e)Standard math.log(n [, base]) / log10(n)log(n) is natural log (T-SQL trap handled); log10 base-10.log(n [, base]) / log10(n)log(n) is natural log (T-SQL trap handled); log10 base-10.pi()3.14159...pi()3.14159...degrees(n) / radians(n)Angle conversion.degrees(n) / radians(n)Angle conversion.sin(n) / cos(n) / tan(n) / cot(n) / asin(n) / acos(n) / atan(n)Trigonometry (radians).sin(n) / cos(n) / tan(n) / cot(n) / asin(n) / acos(n) / atan(n)Trigonometry (radians).atn2(y, x)Two-argument arctangent.atn2(y, x)Two-argument arctangent.rand([seed])Pseudo-random float in [0,1).rand([seed])Pseudo-random float in [0,1).Aggregate / Statistical(4)
stdev(expr) / stdevp(expr)Sample / population standard deviation.stdev(expr) / stdevp(expr)Sample / population standard deviation.var(expr) / varp(expr)Sample / population variance.var(expr) / varp(expr)Sample / population variance.count_big(expr)COUNT returning bigint.count_big(expr)COUNT returning bigint.checksum_agg(int)XOR-based group checksum (not value-identical to SQL Server).checksum_agg(int)XOR-based group checksum (not value-identical to SQL Server).Conversion(3)
try_convert_int/bigint/numeric/float/date/datetime(text)Typed safe conversion; returns NULL on bad input.try_convert_int/bigint/numeric/float/date/datetime(text)Typed safe conversion; returns NULL on bad input.convert_datetime_style(value, style)SQL Server CONVERT date styles (1-126).convert_datetime_style(value, style)SQL Server CONVERT date styles (1-126).format(value, format [, culture]).NET-style numeric formatting (N/F/D/C/P/X and custom masks).format(value, format [, culture]).NET-style numeric formatting (N/F/D/C/P/X and custom masks).JSON(4)
isjson(str)1 if valid JSON, else 0.isjson(str)1 if valid JSON, else 0.json_path_exists(json, path)1 if a JSON path resolves, else 0.json_path_exists(json, path)1 if a JSON path resolves, else 0.json_modify(json, path, value)Update/insert/delete at a JSON path.json_modify(json, path, value)Update/insert/delete at a JSON path.openjson(json)Table-valued expansion (key/value/type), objects and arrays.openjson(json)Table-valued expansion (key/value/type), objects and arrays.Metadata / Catalog(12)
object_id(name) / object_name(id)Resolve object name <-> id.object_id(name) / object_name(id)Resolve object name <-> id.object_schema_name(id) / object_definition(id)Schema name / source text.object_schema_name(id) / object_definition(id)Schema name / source text.objectproperty(id, prop) / columnproperty(id, col, prop)Object/column property flags (IsTable, AllowsNull, ...).objectproperty(id, prop) / columnproperty(id, col, prop)Object/column property flags (IsTable, AllowsNull, ...).col_name(id, n) / col_length(tab, col)Column name / length.col_name(id, n) / col_length(tab, col)Column name / length.schema_id([name]) / schema_name([id])Schema lookups.schema_id([name]) / schema_name([id])Schema lookups.type_id(name) / type_name(id)Type lookups.type_id(name) / type_name(id)Type lookups.db_id([name]) / db_name([id])Database lookups.db_id([name]) / db_name([id])Database lookups.serverproperty(p) / databasepropertyex(db, p)Server / database properties.serverproperty(p) / databasepropertyex(db, p)Server / database properties.app_name()Application name for the session.app_name()Application name for the session.index_col(...) / indexproperty(...)Index metadata.index_col(...) / indexproperty(...)Index metadata.file_id(name) / file_name(id) / filegroup_id(name) / filegroup_name(id)File / filegroup metadata.file_id(name) / file_name(id) / filegroup_id(name) / filegroup_name(id)File / filegroup metadata.stats_date(id, stat_id)Statistics last-updated time.stats_date(id, stat_id)Statistics last-updated time.Security(7)
suser_id([login]) / suser_name([id]) / suser_sname([sid]) / suser_sid([login])Login identity lookups.suser_id([login]) / suser_name([id]) / suser_sname([sid]) / suser_sid([login])Login identity lookups.user_id([name]) / user_name([id])Database-user lookups.user_id([name]) / user_name([id])Database-user lookups.is_member(group) / is_rolemember(role) / is_srvrolemember(role)Role membership tests (mapped to PostgreSQL roles).is_member(group) / is_rolemember(role) / is_srvrolemember(role)Role membership tests (mapped to PostgreSQL roles).has_perms_by_name(obj, class, perm)Permission check via has_table_privilege.has_perms_by_name(obj, class, perm)Permission check via has_table_privilege.original_login()Original session login.original_login()Original session login.loginproperty(login, prop)Login property lookup.loginproperty(login, prop)Login property lookup.permissions([object_id [, column]])Deprecated in SQL Server 2008+ but valid in older releases; returns the documented permission bitmap over PostgreSQL privileges.permissions([object_id [, column]])Deprecated in SQL Server 2008+ but valid in older releases; returns the documented permission bitmap over PostgreSQL privileges.System / Session(12)
isnull(check, replacement)2-argument NULL substitution.isnull(check, replacement)2-argument NULL substitution.iif(cond, t, f)Inline conditional.iif(cond, t, f)Inline conditional.choose(index, v1, v2, ...)1-based pick; out-of-range returns NULL.choose(index, v1, v2, ...)1-based pick; out-of-range returns NULL.newid() / newsequentialid()GUID generation.newid() / newsequentialid()GUID generation.scope_identity() / ident_current(table)Last identity values.scope_identity() / ident_current(table)Last identity values.checksum(...) / binary_checksum(...)Deterministic 32-bit hash (not value-identical to SQL Server).checksum(...) / binary_checksum(...)Deterministic 32-bit hash (not value-identical to SQL Server).host_name() / host_id()Client host info.host_name() / host_id()Client host info.context_info() / set_context_info(bytea)Session context bytes.context_info() / set_context_info(bytea)Session context bytes.session_context(key) / sp_set_session_context(key, value)Key/value session context.session_context(key) / sp_set_session_context(key, value)Key/value session context.formatmessage(msg, args...)printf-style message formatting.formatmessage(msg, args...)printf-style message formatting.parsename('a.b.c.d', n)Extracts the nth name part from the right.parsename('a.b.c.d', n)Extracts the nth name part from the right.compress(bytea|text) / decompress(bytea)GZIP compression, byte-interoperable with SQL Server.compress(bytea|text) / decompress(bytea)GZIP compression, byte-interoperable with SQL Server.Get HexaBridge for Your Migration
HexaBridge is available to customers who migrate their databases using HexaRocket, and to customers of HexaCluster-approved partners. We may open-source HexaBridge in the future - but you don't have to wait. Contact us today and we'll help you get started.
No proprietary PostgreSQL fork required. HexaBridge works as-is on Community PostgreSQL.

