Native Types (Vendor-Specific)
The Right Starting Point
Velocity is designed around the schema-first philosophy: you define your data model once using Velocity's portable DataTypes and let Velocity translate them into optimal native types for every supported database. That approach gives you full portability and the complete Velocity read/write API at every step.
However, the real world is not always schema-first. You may need to integrate with a database that already exists — one designed by someone else, years ago, with types that are entirely native to a single vendor. When you call LoadSchema against such a database, Velocity reads the vendor catalog and maps every column it finds into the closest Velocity DataType. For the common types, this works seamlessly. For the exotic ones it is a best effort, and you need to understand where the cracks are.
Important
If you are starting a new project and have a choice, use schema-first. LoadSchema is a good starting point for understanding an existing database, but it should be the beginning of your work with Velocity — not the end. Review the mapped schema, refine the types, and move toward a portable model that Velocity can manage fully.
What LoadSchema Does
When Velocity loads a schema it reads the vendor's system catalog (e.g. INFORMATION_SCHEMA, DBC.ColumnsV, Oracle's ALL_TAB_COLUMNS) and maps each native column type to a Velocity DataType. Types that Velocity recognizes are mapped to the appropriate portable type. Types that have no reasonable mapping are represented as Unsupported.
Once the schema is in memory you can:
- Inspect the mapped types via the Schema / Table / Column object model
- Query data with Retrieve and read columns using typed accessors on Result
- Write data with AddRecord / UpdateRecords — subject to write-back limitations described below
- Export data to CSV with ExportData
The key limitation is this: reading almost always works; writing does not always work. Many vendor-specific types have no implicit cast from the parameter type that Velocity sends. For those columns you must use raw SQL via RunSql to insert or update data.
Type Mappings by Database
The tables below show every native type covered by Velocity's LoadSchema method, what Velocity maps it to, and whether you can write values to that column through the Velocity API.
Legend
- ✅ Full support — read and write through the Velocity API
- ⚠️ Read only —
Retrieveworks;AddRecord/UpdateRecordrequire raw SQL - 📖 Auto-generated — managed by the database; cannot be set at all
DB2
| Native Type | Velocity Type | .NET Type | Write Support |
|---|---|---|---|
BIGINT |
Long |
long |
✅ |
BLOB, BINARY(n), VARBINARY(n) |
Blob |
byte[] |
✅ |
BOOLEAN |
Boolean |
bool |
✅ |
CHAR(n), CHARACTER(n), GRAPHIC(n), NCHAR(n) |
Char |
string |
✅ |
CLOB, DBCLOB, NCLOB |
Clob |
string |
✅ |
DATE |
Date |
DateOnly |
✅ |
DB2GSE.ST_GEOMETRY |
Geometry |
Geometry |
✅ |
DECIMAL, NUMERIC, DECFLOAT |
Decimal |
decimal |
✅ |
DOUBLE, FLOAT |
Double |
double |
✅ (DB2 FLOAT is double-precision) |
INTEGER, INT |
Integer |
int |
✅ |
JSON |
Clob |
string |
✅ (requires Unicode database codepage) |
LONG VARCHAR |
Clob |
string |
✅ (deprecated but still accepted) |
REAL |
Float |
float |
✅ |
SMALLINT |
Short |
short |
✅ |
TIME |
Time |
TimeOnly |
✅ |
TIMESTAMP |
DateTime |
DateTime |
✅ (DB2 TIMESTAMP has no timezone; use DateTime) |
VARCHAR(n), VARGRAPHIC(n), NVARCHAR(n) |
VarChar |
string |
✅ |
XML |
Clob |
string |
✅ (send as XML string) |
DB2 note: The
TimestampVelocity type (timezone-aware) is not supported by DB2. If your schema usesDataType.Timestamp, DB2 falls back toTIMESTAMP(no timezone). See Datasource Limitations for details.DB2 — Interval: DB2 has no native interval column type. Velocity stores
DataType.Intervalcolumns asBIGINT(100-nanosecond ticks).LoadSchemamaps anyBIGINTcolumn toLong, so an interval column created by Velocity will be loaded back asLong. Use schema-first to preserve theIntervaltype across the lifecycle.
MySQL
| Native Type | Velocity Type | .NET Type | Write Support |
|---|---|---|---|
bigint |
Long |
long |
✅ |
binary, varbinary |
Blob |
byte[] |
✅ |
bit, bool |
Boolean |
bool |
✅ |
char |
Char |
string |
✅ |
date |
Date |
DateOnly |
✅ |
datetime |
DateTime |
DateTime |
✅ |
decimal, numeric |
Decimal |
decimal |
✅ |
double |
Double |
double |
✅ |
enum, set |
VarChar |
string |
✅ (send a valid member value as string) |
float |
Float |
float |
✅ |
geometry, point, linestring, polygon |
Geometry |
Geometry |
✅ |
int, integer, mediumint |
Integer |
int |
✅ |
multipoint, multilinestring, multipolygon, geometrycollection |
Geometry |
Geometry |
✅ |
smallint |
Short |
short |
✅ |
text, mediumtext, longtext, json |
Clob |
string |
✅ |
time |
Time |
TimeOnly |
✅ |
timestamp |
Timestamp |
DateTimeOffset |
✅ |
tinyblob, blob, mediumblob, longblob |
Blob |
byte[] |
✅ |
tinyint |
Byte |
byte |
✅ |
varchar, tinytext |
VarChar |
string |
✅ |
year |
Short |
short |
✅ (send the four-digit year as a short) |
MySQL — Interval: MySQL has no native interval column type. Velocity stores
DataType.Intervalcolumns asBIGINT(100-nanosecond ticks).LoadSchemamaps anyBIGINTcolumn toLong, so an interval column created by Velocity will be loaded back asLong. Use schema-first to preserve theIntervaltype across the lifecycle.
Oracle
| Native Type | Velocity Type | .NET Type | Write Support |
|---|---|---|---|
BFILE |
Blob |
byte[] |
⚠️ External file locator — requires a pre-created DIRECTORY object; cannot insert as raw bytes |
BINARY_DOUBLE, FLOAT(n) |
Double |
double |
✅ |
BINARY_FLOAT |
Float |
float |
✅ |
BLOB |
Blob |
byte[] |
✅ |
BOOLEAN |
Boolean |
bool |
✅ (Oracle 23c and later only) |
CHAR(n), NCHAR(n) |
Char |
string |
✅ |
CLOB, NCLOB, LONG |
Clob |
string |
✅ |
DATE |
Date |
DateOnly |
✅ |
INTERVAL DAY(0) TO SECOND(6) |
Time |
TimeOnly |
✅ |
INTERVAL DAY(9) TO SECOND(6) |
Interval |
TimeSpan |
✅ |
INTERVAL YEAR TO MONTH |
VarChar |
string |
⚠️ Read as string representation; writing requires an explicit INTERVAL literal or CAST |
NUMBER(p,s) |
Decimal |
decimal |
✅ |
RAW(n) |
Blob |
byte[] |
✅ |
ROWID, UROWID |
VarChar |
string |
⚠️ Physical row addresses — system-managed, cannot be set freely |
SDO_GEOMETRY |
Geometry |
Geometry |
✅ |
TIMESTAMP(n) |
DateTime |
DateTime |
✅ |
TIMESTAMP(n) WITH TIME ZONE |
Timestamp |
DateTimeOffset |
✅ |
VARCHAR2(n), VARCHAR(n), NVARCHAR2(n) |
VarChar |
string |
✅ |
XMLTYPE |
Clob |
string |
✅ (send as XML string) |
Oracle note: The
BOOLEANtype was introduced in Oracle 23c. On earlier versions the column will be present in the loaded schema but writes will fail with a type error.
PostgreSQL
| Native Type | Velocity Type | .NET Type | Write Support |
|---|---|---|---|
bigint |
Long |
long |
✅ |
bit(n), bit varying(n) |
Blob |
byte[] |
⚠️ Read as packed byte[]; writing requires explicit ::bit cast |
boolean |
Boolean |
bool |
✅ |
bytea |
Blob |
byte[] |
✅ |
character(n) |
Char |
string |
✅ |
character varying(n), citext |
VarChar |
string |
✅ |
date |
Date |
DateOnly |
✅ |
double precision |
Double |
double |
✅ |
geography (PostGIS) |
Geography |
Geometry |
✅ |
geometry (PostGIS) |
Geometry |
Geometry |
✅ |
hstore |
VarChar |
string |
⚠️ Read as key=>value string; explicit cast required to write |
inet, cidr, macaddr, macaddr8 |
VarChar |
string |
⚠️ Read as string representation; require explicit ::inet / ::cidr cast to write |
int4range, int8range, numrange, tsrange, tstzrange, daterange |
VarChar |
string |
⚠️ Read as string representation; explicit cast required to write |
integer |
Integer |
int |
✅ |
interval |
Interval |
TimeSpan |
✅ Native INTERVAL type; maps directly via NpgsqlDbType.Interval |
json, jsonb, xml |
Clob |
string |
⚠️ PostgreSQL requires an explicit cast for untyped parameters; use raw SQL |
money |
Decimal |
decimal |
✅ |
name |
VarChar |
string |
✅ |
numeric |
Decimal |
decimal |
✅ |
oid |
Long |
long |
✅ |
pg_lsn |
VarChar |
string |
⚠️ Read as string; explicit cast required to write |
point, line, lseg, box, path, polygon, circle |
VarChar |
string |
⚠️ Native PG geometry (not PostGIS); read as string representation; explicit cast required to write |
real |
Float |
float |
✅ |
smallint |
Short |
short |
✅ |
text |
Clob |
string |
✅ |
time with time zone |
Time |
TimeOnly |
✅ |
time without time zone |
Time |
TimeOnly |
✅ |
timestamp with time zone |
Timestamp |
DateTimeOffset |
✅ |
timestamp without time zone |
DateTime |
DateTime |
✅ |
tsvector, tsquery |
VarChar |
string |
⚠️ Read as string; writing requires to_tsvector() / to_tsquery() |
uuid |
VarChar |
string |
⚠️ No implicit varchar → uuid cast; use raw SQL with gen_random_uuid() or ::uuid |
SQL Server
| Native Type | Velocity Type | .NET Type | Write Support |
|---|---|---|---|
bigint |
Long |
long |
✅ |
binary, varbinary, image |
Blob |
byte[] |
✅ |
bit |
Boolean |
bool |
✅ |
char, nchar |
Char |
string |
✅ |
date |
Date |
DateOnly |
✅ |
datetime, datetime2, smalldatetime |
DateTime |
DateTime |
✅ |
datetimeoffset |
Timestamp |
DateTimeOffset |
✅ |
decimal, numeric |
Decimal |
decimal |
✅ |
float |
Double |
double |
✅ |
geography |
Geography |
Geometry |
✅ |
geometry |
Geometry |
Geometry |
✅ |
hierarchyid |
VarChar |
string |
✅ (send as path string, e.g. "/") |
int |
Integer |
int |
✅ |
money, smallmoney |
Decimal |
decimal |
✅ |
real |
Float |
float |
✅ |
rowversion / timestamp |
Blob |
byte[] |
📖 Auto-generated |
smallint |
Short |
short |
✅ |
sql_variant |
VarChar |
string |
✅ (send as string representation) |
time |
Time |
TimeOnly |
✅ |
tinyint |
Byte |
byte |
✅ |
uniqueidentifier |
VarChar |
string |
✅ (send as Guid.ToString()) |
varbinary(max) |
Blob |
byte[] |
✅ |
varchar, nvarchar |
VarChar |
string |
✅ |
varchar(max), nvarchar(max), text, ntext |
Clob |
string |
✅ |
xml |
Clob |
string |
✅ (send as XML string) |
SQL Server — Interval: SQL Server has no native interval column type. Velocity stores
DataType.Intervalcolumns asBIGINT(100-nanosecond ticks).LoadSchemamaps anyBIGINTcolumn toLong, so an interval column created by Velocity will be loaded back asLong. Use schema-first to preserve theIntervaltype across the lifecycle.
SQLite
SQLite uses type affinity rather than strict types. Velocity maps affinity groups consistently and all of them support full read/write through the Velocity API.
| Native Type(s) | Velocity Type | .NET Type | Write Support |
|---|---|---|---|
BLOB, BINARY(n), VARBINARY(n), LONGBLOB |
Blob |
byte[] |
✅ |
BOOLEAN, BOOL |
Boolean |
bool |
✅ |
CHAR(n), NCHAR(n), CHARACTER(n), CHARACTER VARYING(n) |
Char |
string |
✅ |
CLOB, NTEXT, MEDIUMTEXT, LONGTEXT, JSON |
Clob |
string |
✅ |
DATE |
Date |
DateOnly |
✅ |
DATETIME |
DateTime |
DateTime |
✅ |
FLOAT |
Float |
float |
✅ |
GEOMETRY (SpatiaLite) |
Geometry |
Geometry |
✅ |
INT2, SMALLINT, SHORT |
Short |
short |
✅ |
INT8, BIGINT, LONG, UNSIGNED BIG INT |
Long |
long |
✅ |
INTEGER, INT, MEDIUMINT |
Integer |
int |
✅ |
NUMERIC, DECIMAL, NUMBER |
Decimal |
decimal |
✅ |
REAL, DOUBLE, DOUBLE PRECISION |
Double |
double |
✅ |
TEXT, TINYTEXT, STRING, VARCHAR(n), NVARCHAR(n), VARYING CHARACTER(n) |
VarChar |
string |
✅ |
TIME |
Time |
TimeOnly |
✅ |
TIMESTAMP |
Timestamp |
DateTimeOffset |
✅ |
TINYINT, BYTE |
Byte |
byte |
✅ |
UUID |
VarChar |
string |
✅ |
SQLite — Interval: SQLite has no native interval column type. Velocity stores
DataType.Intervalcolumns asBIGINT(100-nanosecond ticks).LoadSchemamaps anyBIGINT-affinity column toLong, so an interval column created by Velocity will be loaded back asLong. Use schema-first to preserve theIntervaltype across the lifecycle.
Teradata
| Native Type | Velocity Type | .NET Type | Write Support |
|---|---|---|---|
BIGINT |
Long |
long |
✅ |
BLOB, BYTE(n), VARBYTE(n) |
Blob |
byte[] |
✅ |
BYTEINT |
Byte |
byte |
✅ |
CHAR(n) |
Char |
string |
✅ |
CLOB, JSON(n) |
Clob |
string |
✅ |
DATE |
Date |
DateOnly |
✅ |
DECIMAL, NUMERIC |
Decimal |
decimal |
✅ |
FLOAT |
Double |
double |
✅ (Teradata FLOAT is double-precision) |
INTEGER |
Integer |
int |
✅ |
INTERVAL DAY TO SECOND (and DAY, DAY TO HOUR, DAY TO MINUTE, HOUR, HOUR TO MINUTE, HOUR TO SECOND, MINUTE, MINUTE TO SECOND, SECOND) |
Interval |
TimeSpan |
✅ |
INTERVAL YEAR TO MONTH, INTERVAL YEAR, INTERVAL MONTH |
VarChar |
string |
⚠️ Year/month intervals have variable-length months and cannot be expressed as TimeSpan; writing requires an explicit INTERVAL literal |
PERIOD(DATE), PERIOD(TIMESTAMP) |
VarChar |
string |
⚠️ Read as string representation; writing requires a PERIOD(...) constructor expression |
SMALLINT |
Short |
short |
✅ |
ST_GEOMETRY |
Geometry |
Geometry |
✅ |
TIME(n) |
Time |
TimeOnly |
✅ |
TIME(n) WITH TIME ZONE |
Timestamp |
DateTimeOffset |
✅ (mapped to Timestamp; timezone offset is preserved) |
TIMESTAMP(n) |
DateTime |
DateTime |
✅ |
TIMESTAMP(n) WITH TIME ZONE |
Timestamp |
DateTimeOffset |
✅ |
VARCHAR(n) |
VarChar |
string |
✅ |
Reading Data Back
When Velocity retrieves rows from a table loaded with LoadSchema, it uses the mapped DataType to choose the right reader path for each column. For the vast majority of types this works transparently:
// Load an existing database schema
using Manager m = new Manager(connection);
Schema schema = m.LoadSchema("legacy_db");
Table orders = schema["orders"];
// Query — works for all mapped types
using var rs = m.Retrieve(new Query().SelectAll(orders));
foreach (Result row in rs)
{
// Standard accessors work for cleanly-mapped types
long id = row.GetFieldLong("order_id");
string reference = row.GetFieldVarChar("reference");
DateOnly placed = row.GetFieldDate("placed_date");
TimeOnly cutoff = row.GetFieldTime("cutoff_time");
// Vendor-specific types mapped to a portable type also work
string xmlData = row.GetFieldClob("config_xml"); // xml → Clob
byte[] thumbnail = row.GetFieldBlob("thumbnail"); // image → Blob
}
For columns that map to Unsupported, GetField returns the raw CLR object from the driver. You will need to handle the casting yourself:
foreach (Column col in orders.Columns)
{
if (col.DataType == DataType.Unsupported)
{
object raw = row.GetField(col.Name);
// Cast or handle the vendor-specific CLR type manually
}
}
Writing Data Back
Writing back through the Velocity API works whenever the database accepts an untyped (or text/binary) parameter for the column type. Where it fails is where the database requires an explicit type cast and does not accept an implicit one.
What works everywhere
All standard Velocity data types write without issue — the tables above mark these ✅. This covers the majority of columns in most real-world schemas.
Record r = new Record(orders);
r.SetFieldLong("order_id", 42L);
r.SetFieldVarChar("reference", "ORD-42");
r.SetFieldDate("placed_date", DateOnly.FromDateTime(DateTime.UtcNow));
r.SetFieldTime("cutoff_time", new TimeOnly(17, 0, 0));
r.SetFieldDecimal("total", 199.99M);
m.AddRecord(r);
What requires raw SQL
For columns marked ⚠️ in the tables above, use RunSql with a native SQL literal:
// PostgreSQL: json/jsonb columns need an explicit cast
m.RunSql(@"
INSERT INTO legacy.events (id, payload)
VALUES (1, '{""action"":""login""}'::jsonb)");
// PostgreSQL: uuid column
m.RunSql(@"
INSERT INTO legacy.sessions (session_id, user_id)
VALUES (gen_random_uuid(), 42)");
// Oracle: INTERVAL YEAR TO MONTH
m.RunSql(@"
INSERT INTO contracts (contract_id, duration)
VALUES (1, INTERVAL '1-06' YEAR TO MONTH)");
// Teradata: PERIOD column
m.RunSql(@"
INSERT INTO projects (project_id, active_period)
VALUES (1, PERIOD(DATE '2024-01-01', DATE '2024-12-31'))");
The Unsupported DataType
When LoadSchema encounters a native type it has no mapping for, it sets the column's DataType to Unsupported. The column still appears in the in-memory schema and the raw value is still returned by GetField, but:
- No typed accessor (
GetFieldLong,GetFieldDate, etc.) will work for that column - You cannot write to that column through the Velocity write API
- The column will not survive a round-trip through
BuildSchema— it will be skipped
Encountering Unsupported columns is a signal that those columns require vendor-specific handling entirely outside Velocity's portable layer.
When Velocity Is Not the Right Tool
Velocity is the right tool when portability is a goal — when your data model either already is, or can be expressed as, a set of types that make sense across multiple relational databases. The schema-first approach and the cross-database portability it enables are what Velocity is designed to deliver.
If your application relies heavily on vendor-specific types that have no portable equivalent, Velocity may not be the right choice for those parts of your system:
- PostgreSQL-specific workloads built around
hstore, native range types, network address types,tsvector/tsquery, or advancedjsonboperators are most naturally served by a tool that understands PostgreSQL natively — raw ADO.NET with the Npgsql driver, or a PostgreSQL-specific data access layer. - Oracle-specific workloads that depend on
SDO_GEOMETRYtopology analysis,XMLDBrepository features,BFILEexternal storage, or fine-grainedINTERVALarithmetic are best handled directly through the Oracle ADO.NET provider. - SQL Server-specific workloads that rely on
hierarchyidtraversal functions,spatialmethod chains, orsql_variantdynamic typing are best served by a SQL Server-specific data access layer built on top of the Microsoft ADO.NET provider. - Teradata-specific workloads that depend heavily on
PERIODtemporal logic,INTERVALarithmetic, or TD-specific analytic functions belong in a Teradata-native SQL layer.
Using Velocity in a mixed architecture — where Velocity manages the portable parts of your data model and native drivers handle the vendor-specific parts — is a perfectly valid pattern. Velocity does not need to own every table in your database.
The guiding question: If you had to move this database to a different vendor tomorrow, which columns would cause the most pain? Those are the columns that Velocity cannot help you with if you stay in them deep. The columns that map cleanly — integers, strings, dates, booleans, decimals, blobs — are the ones where Velocity gives you full portability with no compromise.