Bram Kuijvenhoven wrote:
I'll be right back with a version of odbcconn that tries giving a more
meaningful error using the sqlGetTypeInfo API call. Arí can also use
this to give us more information, that can help us finding out what is
actually going wrong.
Here's the patch.
odbcconn.pas:
- create a more verbose error when an unknown or unsupported field type is
encountered
odbcsql.inc:
- added some SQL_DESC_* constants to be used with the SQLGetColAttribute call
I tested it with a MySQL database, temporarily setting DataType:=ftUnknown, and
it worked. I suggest that Arí applies uses this patch and sends us the detailed
error message. The patch can also be applied to svn trunk safely as far as I'm
concerned.
Note: to test the patch without having to compile fpc yourself etc., you can
temporarily copy the patched odbcsql.pas and odbcsql.inc /and/ odbcsqldyn.pp to
your projects' directory. Don't forget to remove it later, when your fpc
compiler has the patch applied to it!
Regards,
Bram
Index: fpc/fcl/db/sqldb/odbc/odbcconn.pas
===================================================================
--- fpc/fcl/db/sqldb/odbc/odbcconn.pas (revision 3028)
+++ fpc/fcl/db/sqldb/odbc/odbcconn.pas (working copy)
@@ -126,7 +126,7 @@
implementation
uses
- Math; // for the Min proc
+ Math, DBConst;
const
DefaultEnvironment:TODBCEnvironment = nil;
@@ -608,14 +608,15 @@
procedure TODBCConnection.AddFieldDefs(cursor: TSQLCursor; FieldDefs:
TFieldDefs);
const
- ColNameDefaultLength = 40; // should be > 0, because an ansistring of length
0 is a nil pointer instead of a pointer to a #0
+ ColNameDefaultLength = 40; // should be > 0, because an ansistring of
length 0 is a nil pointer instead of a pointer to a #0
+ TypeNameDefaultLength = 80; // idem
var
ODBCCursor:TODBCCursor;
ColumnCount:SQLSMALLINT;
i:integer;
- ColNameLength,DataType,DecimalDigits,Nullable:SQLSMALLINT;
+ ColNameLength,TypeNameLength,DataType,DecimalDigits,Nullable:SQLSMALLINT;
ColumnSize:SQLUINTEGER;
- ColName:string;
+ ColName,TypeName:string;
FieldType:TFieldType;
FieldSize:word;
begin
@@ -689,7 +690,7 @@
SQL_TYPE_TIME: begin FieldType:=ftTime; FieldSize:=0; end;
SQL_TYPE_TIMESTAMP:begin FieldType:=ftDateTime; FieldSize:=0; end;
{ SQL_TYPE_UTCDATETIME:FieldType:=ftUnknown;}
-{ SQL_TYPE_UTCTIME: FieldType:=ftUnknown; }
+{ SQL_TYPE_UTCTIME: FieldType:=ftUnknown;}
{ SQL_INTERVAL_MONTH: FieldType:=ftUnknown;}
{ SQL_INTERVAL_YEAR: FieldType:=ftUnknown;}
{ SQL_INTERVAL_YEAR_TO_MONTH: FieldType:=ftUnknown;}
@@ -707,13 +708,49 @@
else
begin FieldType:=ftUnknown; FieldSize:=ColumnSize; end
end;
-
+
if (FieldType in [ftString,ftFixedChar]) and // field types mapped to
TStringField
(FieldSize >= dsMaxStringSize) then
begin
FieldSize:=dsMaxStringSize-1;
end;
+
+ if FieldType=ftUnknown then // if unknown field type encountered, try
finding more specific information about the ODBC SQL DataType
+ begin
+ SetLength(TypeName,TypeNameDefaultLength); // also garantuees uniqueness
+
+ ODBCCheckResult(
+ SQLColAttribute(ODBCCursor.FSTMTHandle, // statement handle
+ i, // column number
+ SQL_DESC_TYPE_NAME, // FieldIdentifier indicating
the datasource dependent data type name (useful for diagnostics)
+ @(TypeName[1]), // default buffer
+ TypeNameDefaultLength+1, // and its length; we include
the #0 terminating any ansistring of Length > 0 in the buffer
+ @TypeNameLength, // actual type name length
+ nil // no need for a pointer to
return a numeric attribute at
+ ),
+ SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not get
datasource dependent type name for column %s.',[ColName])
+ );
+ // truncate buffer or make buffer long enough for entire column name
(note: the call is the same for both cases!)
+ SetLength(TypeName,TypeNameLength);
+ // check whether entire column name was returned
+ if TypeNameLength>TypeNameDefaultLength then
+ begin
+ // request column name with buffer that is long enough
+ ODBCCheckResult(
+ SQLColAttribute(ODBCCursor.FSTMTHandle, // statement handle
+ i, // column number
+ SQL_DESC_TYPE_NAME, // FieldIdentifier
indicating the datasource dependent data type name (useful for diagnostics)
+ @(TypeName[1]), // buffer
+ TypeNameLength+1, // buffer size
+ @TypeNameLength, // actual length
+ nil), // no need for a pointer to
return a numeric attribute at
+ SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not get
datasource dependent type name for column %s.',[ColName])
+ );
+ end;
+ DatabaseErrorFmt('Column %s has an unknown or unsupported column type.
Datasource dependent type name: %s. ODBC SQL data type code: %d.', [ColName,
TypeName, DataType]);
+ end;
+
// add FieldDef
TFieldDef.Create(FieldDefs, ColName, FieldType, FieldSize, False, i);
end;
Index: fpc/packages/base/odbc/odbcsql.inc
===================================================================
--- fpc/packages/base/odbc/odbcsql.inc (revision 3028)
+++ fpc/packages/base/odbc/odbcsql.inc (working copy)
@@ -964,11 +964,39 @@
{$ifdef ODBCVER3}
SQL_COLUMN_DRIVER_START = 1000;
{$endif} { ODBCVER >= 0x0300 }
- SQL_DESC_AUTO_UNIQUE_VALUE = SQL_COLUMN_AUTO_INCREMENT;
- SQL_DESC_BASE_COLUMN_NAME = 22;
- SQL_DESC_BASE_TABLE_NAME = 23;
- SQL_DESC_TABLE_NAME = SQL_COLUMN_TABLE_NAME;
+ { SQLColAttribute defines }
+{$ifdef ODBCVER3}
+ SQL_DESC_ARRAY_SIZE = 20;
+ SQL_DESC_ARRAY_STATUS_PTR = 21;
+ SQL_DESC_AUTO_UNIQUE_VALUE = SQL_COLUMN_AUTO_INCREMENT;
+ SQL_DESC_BASE_COLUMN_NAME = 22;
+ SQL_DESC_BASE_TABLE_NAME = 23;
+ SQL_DESC_BIND_OFFSET_PTR = 24;
+ SQL_DESC_BIND_TYPE = 25;
+ SQL_DESC_CASE_SENSITIVE = SQL_COLUMN_CASE_SENSITIVE;
+ SQL_DESC_CATALOG_NAME = SQL_COLUMN_QUALIFIER_NAME;
+ SQL_DESC_CONCISE_TYPE = SQL_COLUMN_TYPE;
+ SQL_DESC_DATETIME_INTERVAL_PRECISION = 26;
+ SQL_DESC_DISPLAY_SIZE = SQL_COLUMN_DISPLAY_SIZE;
+ SQL_DESC_FIXED_PREC_SCALE = SQL_COLUMN_MONEY;
+ SQL_DESC_LABEL = SQL_COLUMN_LABEL;
+ SQL_DESC_LITERAL_PREFIX = 27;
+ SQL_DESC_LITERAL_SUFFIX = 28;
+ SQL_DESC_LOCAL_TYPE_NAME = 29;
+ SQL_DESC_MAXIMUM_SCALE = 30;
+ SQL_DESC_MINIMUM_SCALE = 31;
+ SQL_DESC_NUM_PREC_RADIX = 32;
+ SQL_DESC_PARAMETER_TYPE = 33;
+ SQL_DESC_ROWS_PROCESSED_PTR = 34;
+ SQL_DESC_SCHEMA_NAME = SQL_COLUMN_OWNER_NAME;
+ SQL_DESC_SEARCHABLE = SQL_COLUMN_SEARCHABLE;
+ SQL_DESC_TYPE_NAME = SQL_COLUMN_TYPE_NAME;
+ SQL_DESC_TABLE_NAME = SQL_COLUMN_TABLE_NAME;
+ SQL_DESC_UNSIGNED = SQL_COLUMN_UNSIGNED;
+ SQL_DESC_UPDATABLE = SQL_COLUMN_UPDATABLE;
+{$endif}
+
//* SQLEndTran() options */
SQL_COMMIT = 0;
SQL_ROLLBACK = 1;