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;

Reply via email to