From 227f408df9df51ff58aee0599c539cb4f392ec9c Mon Sep 17 00:00:00 2001 From: ondrej Date: Sun, 29 Mar 2020 06:46:42 +0000 Subject: [PATCH] Merge revisions: r44040, r43881, r43700, r43699, r43698, r43602 Revision: 44040 Author: ondrej Date: Sonntag, 26. Januar 2020 18:49:08 Message: fcl-db: allow 0 length in binary field (MSSQL can have a null/0 field length in a view) ---- Modified : /trunk/packages/fcl-db/src/base/fields.inc Revision: 43881 Author: ondrej Date: Dienstag, 7. Januar 2020 10:41:20 Message: odbc: fix international column names ---- Modified : /trunk/packages/fcl-db/src/sqldb/odbc/odbcconn.pas Modified : /trunk/packages/fcl-db/src/sqldb/sqldb.pp Revision: 43700 Author: ondrej Date: Donnerstag, 19. Dezember 2019 18:43:34 Message: sqldb: odbc: use ANSI codepage for connection as default ---- Modified : /trunk/packages/fcl-db/src/sqldb/odbc/odbcconn.pas Revision: 43699 Author: ondrej Date: Donnerstag, 19. Dezember 2019 17:54:36 Message: sqldb: odbc: send SQL to ODBC as WideString to support FPC 3.0.x character conversion ---- Modified : /trunk/packages/fcl-db/src/sqldb/odbc/odbcconn.pas Revision: 43698 Author: ondrej Date: Donnerstag, 19. Dezember 2019 17:41:57 Message: odbc: fix passing ANSI parameters with FPC character conversion ---- Modified : /trunk/packages/fcl-db/src/sqldb/odbc/odbcconn.pas Revision: 43602 Author: ondrej Date: Donnerstag, 28. November 2019 13:59:13 Message: fcl-db: ODBC: allow NULL parameters (TParam.Value := Null sets type to ftUnknown) ---- Modified : /trunk/packages/fcl-db/src/sqldb/odbc/odbcconn.pas git-svn-id: branches/fixes_3_2@44391 - --- packages/fcl-db/src/base/fields.inc | 2 +- packages/fcl-db/src/sqldb/odbc/odbcconn.pas | 78 ++++++++++++--------- packages/fcl-db/src/sqldb/sqldb.pp | 1 + 3 files changed, 48 insertions(+), 33 deletions(-) diff --git a/packages/fcl-db/src/base/fields.inc b/packages/fcl-db/src/base/fields.inc index 620844b2a7..39aea71f65 100644 --- a/packages/fcl-db/src/base/fields.inc +++ b/packages/fcl-db/src/base/fields.inc @@ -2389,7 +2389,7 @@ class procedure TBinaryField.CheckTypeSize(AValue: Longint); begin // Just check for really invalid stuff; actual size is // dependent on the record... - If AValue<1 then + If AValue<0 then // MSSQL can have a null/0 field length in a view DatabaseErrorFmt(SInvalidFieldSize,[AValue]); end; diff --git a/packages/fcl-db/src/sqldb/odbc/odbcconn.pas b/packages/fcl-db/src/sqldb/odbc/odbcconn.pas index 92caba765e..95d31dd5c8 100644 --- a/packages/fcl-db/src/sqldb/odbc/odbcconn.pas +++ b/packages/fcl-db/src/sqldb/odbc/odbcconn.pas @@ -72,6 +72,7 @@ type procedure FreeParamBuffers(ODBCCursor:TODBCCursor); protected // Overrides from TSQLConnection + function GetConnectionCharSet: string; override; function GetHandle:pointer; override; // - Connect/disconnect procedure DoInternalConnect; override; @@ -400,6 +401,7 @@ var BufferLength, StrLenOrInd: SQLLEN; CType, SqlType, DecimalDigits:SQLSMALLINT; APD: SQLHDESC; + BytesVal: TBytes; begin // Note: it is assumed that AParams is the same as the one passed to PrepareStatement, in the sense that // the parameters have the same order and names @@ -440,43 +442,27 @@ begin SqlType:=SQL_BIGINT; ColumnSize:=19; end; - ftString, ftFixedChar, ftBlob, ftMemo, ftGuid, - ftBytes, ftVarBytes: + ftBlob, ftBytes, ftVarBytes: begin - StrVal:=AParams[ParamIndex].AsString; - StrLenOrInd:=Length(StrVal); - if StrVal='' then //HY104 + BytesVal:=AParams[ParamIndex].AsBytes; + StrLenOrInd:=Length(BytesVal); + if Length(BytesVal)=0 then //HY104 begin - StrVal:=#0; + BytesVal:=[0]; StrLenOrInd:=SQL_NTS; end; - PVal:=@StrVal[1]; - Size:=Length(StrVal); + PVal:=@BytesVal[0]; + Size:=Length(BytesVal); ColumnSize:=Size; BufferLength:=Size; + CType:=SQL_C_BINARY; case AParams[ParamIndex].DataType of - ftBytes, ftVarBytes: - begin - CType:=SQL_C_BINARY; - SqlType:=SQL_VARBINARY; - end; - ftBlob: - begin - CType:=SQL_C_BINARY; + ftBytes, ftVarBytes: SqlType:=SQL_VARBINARY; + else // ftBlob SqlType:=SQL_LONGVARBINARY; - end; - ftMemo: - begin - CType:=SQL_C_CHAR; - SqlType:=SQL_LONGVARCHAR; - end - else // ftString, ftFixedChar - begin - CType:=SQL_C_CHAR; - SqlType:=SQL_VARCHAR; - end; end; end; + ftString, ftFixedChar, ftMemo, ftGuid, // string parameters must be passed as widestring to support FPC 3.0.x character conversion ftWideString, ftFixedWideChar, ftWideMemo: begin WideStrVal:=AParams[ParamIndex].AsWideString; @@ -492,7 +478,7 @@ begin BufferLength:=Size; CType:=SQL_C_WCHAR; case AParams[ParamIndex].DataType of - ftWideMemo: SqlType:=SQL_WLONGVARCHAR; + ftMemo, ftWideMemo: SqlType:=SQL_WLONGVARCHAR; else SqlType:=SQL_WVARCHAR; end; end; @@ -563,7 +549,19 @@ begin CType:=SQL_C_BIT; SqlType:=SQL_BIT; ColumnSize:=Size; - end + end; + ftUnknown: + begin + if AParams[ParamIndex].IsNull then // Null variant is stored as ftUnknown - send it over as TINYINT (it doesn't really matter what type) + begin + PVal:=nil; + Size:=0; + CType:=SQL_C_TINYINT; + SqlType:=SQL_TINYINT; + ColumnSize:=Size; + end else + raise EDataBaseError.CreateFmt('Not-null unknown Parameter (index %d) is not supported.',[ParamIndex]); + end; else raise EDataBaseError.CreateFmt('Parameter %d is of type %s, which not supported yet',[ParamIndex, Fieldtypenames[AParams[ParamIndex].DataType]]); end; @@ -572,7 +570,8 @@ begin StrLenOrInd:=SQL_NULL_DATA; Buf:=GetMem(Size+SizeOf(StrLenOrInd)); - Move(PVal^, Buf^, Size); + if Size>0 then + Move(PVal^, Buf^, Size); if StrLenOrInd<>0 then begin PStrLenOrInd:=Buf + Size; @@ -618,6 +617,13 @@ begin SetLength(ODBCCursor.FParamBuf,0); end; +function TODBCConnection.GetConnectionCharSet: string; +begin + Result := inherited GetConnectionCharSet; + if Result='' then + Result := TEncoding.ANSI.EncodingName; // by default, ODBC talks in ANSI, which can be different from CP_ACP (DefaultSystemCodePage) +end; + function TODBCConnection.GetHandle: pointer; begin // I'm not sure whether this is correct; perhaps we should return nil @@ -737,6 +743,7 @@ end; procedure TODBCConnection.PrepareStatement(cursor: TSQLCursor; ATransaction: TSQLTransaction; buf: string; AParams: TParams); var ODBCCursor:TODBCCursor; + wbuf: widestring; begin ODBCCursor:=cursor as TODBCCursor; @@ -759,8 +766,9 @@ begin ODBCCursor.FQuery:=Buf; if not (ODBCCursor.FSchemaType in [stTables, stSysTables, stColumns, stProcedures, stIndexes]) then begin + wbuf := buf; ODBCCheckResult( - SQLPrepare(ODBCCursor.FSTMTHandle, PChar(buf), Length(buf)), + SQLPrepareW(ODBCCursor.FSTMTHandle, PWideChar(wbuf), Length(wbuf)), SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not prepare statement.' ); end; @@ -913,7 +921,7 @@ var i:integer; ColNameLength,TypeNameLength,DataType,DecimalDigits,Nullable:SQLSMALLINT; ColumnSize:SQLULEN; - ColName,TypeName:string; + ColName,TypeName:RawByteString; FieldType:TFieldType; FieldSize:word; AutoIncAttr, FixedPrecScale, Unsigned, Updatable: SQLLEN; @@ -962,6 +970,9 @@ begin SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not get column name for column %d.',[i] ); end; + // ColName is received in ANSI - convert to DefaultSystemCodePage + SetCodePage(ColName, CodePage, False); + SetCodePage(ColName, DefaultSystemCodePage, True); // convert type // NOTE: I made some guesses here after I found only limited information about TFieldType; please report any problems @@ -1115,6 +1126,9 @@ begin SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not get datasource dependent type name for column %s.',[ColName] ); end; + // TypeName is received in ANSI - convert to DefaultSystemCodePage + SetCodePage(TypeName, CodePage, False); + SetCodePage(TypeName, DefaultSystemCodePage, True); 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; diff --git a/packages/fcl-db/src/sqldb/sqldb.pp b/packages/fcl-db/src/sqldb/sqldb.pp index 90e92bf469..89ebbcc51a 100644 --- a/packages/fcl-db/src/sqldb/sqldb.pp +++ b/packages/fcl-db/src/sqldb/sqldb.pp @@ -267,6 +267,7 @@ type Property Statements : TThreadList Read FStatements; property Port: cardinal read GetPort write SetPort; + property CodePage: TSystemCodePage read FCodePage; public constructor Create(AOwner: TComponent); override; destructor Destroy; override;