From ddd9f345f174b8b4e808bd5aecc8b2a285249b7f Mon Sep 17 00:00:00 2001 From: Thomas Stutz Date: Fri, 5 Jun 2026 11:19:39 +0200 Subject: [PATCH 1/3] Fix CRUD operations in SQLRDD, tested with bBrowser --- src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg | 67 ++++++++++++------- .../XSharp.SQLRdd/RDD/SQLRDD-Private.prg | 19 ++++-- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg index 728aff5956..fa5899c044 100644 --- a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg +++ b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg @@ -173,6 +173,7 @@ partial class SQLRDD inherit Workarea self:_updatableColumns := List{} self:_keyColumns := List{} self:_numHiddenColumns := 0 + var colNo := 0 foreach var oCol in _oTd:Columns var oField := oCol:ColumnInfo if ! String.Equals(oField:ColumnName, oField:Name, StringComparison.OrdinalIgnoreCase) @@ -180,13 +181,13 @@ partial class SQLRDD inherit Workarea endif if oCol:ColumnFlags:HasFlag(SqlDbColumnFlags.Recno) - self:_recnoColumNo := oField:Ordinal + self:_recnoColumNo := colNo oField:Flags |= DBFFieldFlags.AutoIncrement oField:Flags |= DBFFieldFlags.System self:_numHiddenColumns += 1 lhasRecnoColumn := true elseif oCol:ColumnFlags:HasFlag(SqlDbColumnFlags.Deleted) - self:_deletedColumnNo := oField:Ordinal + self:_deletedColumnNo := colNo self:_deletedColumnIsLogic := oField:FieldType == DbFieldType.Logic oField:Flags |= DBFFieldFlags.System self:_numHiddenColumns += 1 @@ -208,6 +209,7 @@ partial class SQLRDD inherit Workarea self:_updatableColumns:Add(oField) endif ENDIF + colNo++ next if !lhasRecnoColumn .and. self:_tableMode = TableMode.Table @@ -240,6 +242,7 @@ partial class SQLRDD inherit Workarea SELF:_hasEOF := TRUE endif _command:CommandText := cQuery + self:_ForceOpen() return true end method @@ -258,7 +261,6 @@ partial class SQLRDD inherit Workarea var key := (dword) self:_builder:GetNextKey() var row := self:DataTable:NewRow() self:DataTable:Rows:Add(row) - _updatedRows:Add(row) if _emptyValues == null self:_GetEmptyValues() endif @@ -270,10 +272,10 @@ partial class SQLRDD inherit Workarea row[c] := SELF:_HandleNullDate(values[c:Ordinal],c) endif next - // if self:_recnoColumNo > -1 - // self:_recordKeyCache:Add(key, (DWORD) SELF:RowCount-1) - // endif - SELF:_rowNumber := SELF:DataTable:Rows:Count + self:_ExecuteInsertStatement(row) + row:AcceptChanges() + _updatedRowIds:Add((int)row[self:_recnoColumNo]) + SELF:RowNumber := SELF:DataTable:Rows:Count self:_serverReccount += 1 SELF:_SetBOF(FALSE) SELF:_SetEOF(FALSE) @@ -289,8 +291,11 @@ partial class SQLRDD inherit Workarea /// When the area is in Tablemode, and no data has been read before, then this will trigger fetching the data from the database /// override method GetValue(nFldPos as int) as object + if self:CurrentRow == null + return null + endif + // nFldPos is 1 based, the RDD compiles with /az+ - SELF:_ForceOpen() if nFldPos > 0 .and. nFldPos <= self:RealFieldCount var col := self:_GetColumn(nFldPos) nFldPos -= 1 @@ -363,8 +368,8 @@ partial class SQLRDD inherit Workarea var col := self:_GetColumn(nFldPos) if SELF:_updatableColumns:Contains(col) var row := SELF:CurrentRow - if !_updatedRows:Contains(row) - _updatedRows:Add(row) + if !_updatedRowIds:Contains((int)row[self:_recnoColumNo]) + _updatedRowIds:Add((int)row[self:_recnoColumNo]) endif row[nFldPos-1] := SELF:_HandleNullDate(oValue,self:DataTable:Columns[nFldPos-1]) @@ -380,6 +385,7 @@ partial class SQLRDD inherit Workarea /// Write the contents of a work area's memory to the data store (usually a disk). /// override method GoCold() as logic + local row as DataRow var current := SELF:CurrentRow if current == null return false @@ -398,8 +404,14 @@ partial class SQLRDD inherit Workarea return false endif - foreach var row in _updatedRows + foreach var rowId in _updatedRowIds try + foreach tableRow as DataRow in self:DataTable:Rows + if (int)tableRow[self:_recnoColumNo] = rowId + row := tableRow + endif + next + // Check row lock dbLockInfo:RecId := row[_oTd:RecnoColumn] SELF:CheckLock(dbLockInfo, StringBuilder{}, myLock, otherLock) @@ -460,12 +472,12 @@ partial class SQLRDD inherit Workarea else self:DataTable:RejectChanges() endif - _updatedRows:Clear() - // TODO: thomas optimize. Change reccount when adding or deleting a row above instead of reloading data from DB with _GetRecCount + _updatedRowIds:Clear() self:_GetRecCount() endif return lOk end method + override method GoHot() as logic return true end method @@ -502,9 +514,7 @@ partial class SQLRDD inherit Workarea row[_deletedColumnNo] := 1 endif endif - // Must position the DBF on the right row for the deletion - super:GoTo((DWORD) SELF:RowNumber) - return super:Delete() + return true end method /// Remove the deletion marker from the row at the current cursor position. @@ -673,16 +683,18 @@ partial class SQLRDD inherit Workarea RETURN SELF:_GotoRow((LONG) nRec) ENDIF // Check to see if we have the record in the current buffer - var nCount := SELF:DataTable:Rows:Count - FOR VAR nRow := 1 to nCount - SELF:RowNumber := nRow +1 - IF SELF:RecNo == nRec + var rowIndex := 1 + foreach oRow as DataRow in SELF:DataTable:Rows + if (int)oRow[self:_recnoColumNo] = nRec + SELF:RowNumber := rowIndex SELF:_SetEOF(FALSE) SELF:_SetBOF(FALSE) SELF:_Found := TRUE - RETURN TRUE + return true endif - NEXT + rowIndex++ + next + SELF:_GotoRecord(nRec) SELF:_CheckEofBof() RETURN TRUE @@ -700,7 +712,7 @@ partial class SQLRDD inherit Workarea return (DWORD) SELF:RowNumber endif var row := SELF:CurrentRow - if ! SELF:EoF .and. row != null + if ! SELF:_EoF .and. row != null var obj := row[self:_recnoColumNo] return Convert.ToUInt32(obj) endif @@ -825,6 +837,10 @@ partial class SQLRDD inherit Workarea RETURN SUPER:SetFilter(info) end method + public override method Flush() as logic + RETURN SELF:GoCold() + end method + #region Lock / Unlock public override method Lock(lockInfo ref DbLockInfo) as logic @@ -917,6 +933,11 @@ partial class SQLRDD inherit Workarea return false end try + // In ADS this happens in here: ACE.AdsUnlockRecord(SELF:_Table, dwRecno) + if (self:Deleted) + self:_ExecuteDeleteStatement(CurrentRow) + endif + return true end method diff --git a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg index 2bc962c12d..43dc8e9a67 100644 --- a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg +++ b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg @@ -39,9 +39,9 @@ partial class SQLRDD private _emptyValues as object[] private _updatableColumns as List private _keyColumns as List - private _updatedRows as List + private _updatedRowIds as List private _orderBagList as List - private _rowNumber as long + private _rowNumber as long /// /// 0 based Column Number for the column that has the deleted flag @@ -127,7 +127,7 @@ partial class SQLRDD SELF:_currentPageNo := 1 SELF:_firstPageNo := 1 self:_trimValues := true // trim String Valuess - SELF:_updatedRows := List{} + SELF:_updatedRowIds := List{} SELF:_keyColumns := List{} SELF:_updatableColumns := List{} SELF:_orderBagList := List{} @@ -136,7 +136,8 @@ partial class SQLRDD end constructor destructor() - Command:Close() + Command:Dispose() + _connection:Dispose() end destructor internal method _ClearTable() AS VOID @@ -365,7 +366,7 @@ partial class SQLRDD _command:AddParameter(name, row[c]) ++iCounter next - if ! lColumnChanged + if !lColumnChanged .and. !self:_oTd:UpdateAllColumns // no columns changed, so we do not update this row return TRUE endif @@ -410,6 +411,7 @@ partial class SQLRDD end method private method _ExecuteDeleteStatement(row as DataRow) as logic + // TODO: check config if logic or physical delete _command:ClearParameters() var strWhere := SELF:_GetWhereClause(row) var sb := StringBuilder{} @@ -580,7 +582,10 @@ partial class SQLRDD SELF:RowNumber := SELF:DataTable:Rows:Count + 1 foreach row as DataRow in newTable:Rows - SELF:DataTable:Rows:Add(row:ItemArray) + var newRow := SELF:DataTable:NewRow() + newRow:ItemArray := row:ItemArray + SELF:DataTable:Rows:Add(newRow) + newRow:AcceptChanges() next else SELF:RowNumber := newTable:Rows:Count @@ -590,6 +595,7 @@ partial class SQLRDD var newRow := SELF:DataTable:NewRow() newRow:ItemArray := row:ItemArray SELF:DataTable:Rows:InsertAt(newRow, 0) + newRow:AcceptChanges() next endif if lForward .and. newTable:Rows:Count < _oTd:PageSize @@ -601,7 +607,6 @@ partial class SQLRDD return result PRIVATE METHOD _GotoRecord(nRec as DWORD) AS LOGIC - if SELF:DataTable:Rows:Count < 1 // Brute walk SELF:_command:CommandText := _builder:BuildRowNumberStatement(nRec) From d0fc70d421769176ece6d3427481fb4a629fba07 Mon Sep 17 00:00:00 2001 From: Thomas Stutz Date: Wed, 10 Jun 2026 13:54:05 +0200 Subject: [PATCH 2/3] SQLRDD small fixes for update, insert and paging with order --- src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg | 82 +++---------- .../XSharp.SQLRdd/RDD/SQLRDD-Private.prg | 112 ++++++++++++++---- 2 files changed, 107 insertions(+), 87 deletions(-) diff --git a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg index fa5899c044..0590954b55 100644 --- a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg +++ b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg @@ -41,7 +41,7 @@ partial class SQLRDD inherit Workarea public property RowCount as INT => iif(Self:DataTable == null, 0, Self:DataTable:Rows:Count) /// The current rownumber in the buffer (DataTable). - public property RowNumber as INT GET _rowNumber INTERNAL SET _rowNumber := value + public property RowNumber as INT GET _rowNumber private SET _rowNumber := value /// The current row in the buffer (DataTable). /// When the server is at EOF then the phantomrow is returned. @@ -274,7 +274,7 @@ partial class SQLRDD inherit Workarea next self:_ExecuteInsertStatement(row) row:AcceptChanges() - _updatedRowIds:Add((int)row[self:_recnoColumNo]) + _updatedRecNos:Add((int)row[self:_recnoColumNo]) SELF:RowNumber := SELF:DataTable:Rows:Count self:_serverReccount += 1 SELF:_SetBOF(FALSE) @@ -368,8 +368,8 @@ partial class SQLRDD inherit Workarea var col := self:_GetColumn(nFldPos) if SELF:_updatableColumns:Contains(col) var row := SELF:CurrentRow - if !_updatedRowIds:Contains((int)row[self:_recnoColumNo]) - _updatedRowIds:Add((int)row[self:_recnoColumNo]) + if !_updatedRecNos:Contains((int)row[self:_recnoColumNo]) + _updatedRecNos:Add((int)row[self:_recnoColumNo]) endif row[nFldPos-1] := SELF:_HandleNullDate(oValue,self:DataTable:Columns[nFldPos-1]) @@ -385,7 +385,6 @@ partial class SQLRDD inherit Workarea /// Write the contents of a work area's memory to the data store (usually a disk). /// override method GoCold() as logic - local row as DataRow var current := SELF:CurrentRow if current == null return false @@ -404,64 +403,8 @@ partial class SQLRDD inherit Workarea return false endif - foreach var rowId in _updatedRowIds - try - foreach tableRow as DataRow in self:DataTable:Rows - if (int)tableRow[self:_recnoColumNo] = rowId - row := tableRow - endif - next - - // Check row lock - dbLockInfo:RecId := row[_oTd:RecnoColumn] - SELF:CheckLock(dbLockInfo, StringBuilder{}, myLock, otherLock) - if otherLock - lOk := false - loop - endif - - if !myLock - SELF:Lock(ref dbLockInfo) - endif - - lOk := true - if super:Deleted - local wasNew := false as logic - // Append from may add deleted rows - if row:RowState.HasFlag(DataRowState.Added) - lOk := SELF:_ExecuteInsertStatement(row) - row:AcceptChanges() - wasNew := true - endif - if self:_deletedColumnNo > -1 - if !wasNew - // already written with _deletedColumnNo with the correct value - lOk := SELF:_ExecuteUpdateStatement(row) - if lOk - row:AcceptChanges() - endif - endif - else - lOk := SELF:_ExecuteDeleteStatement(row) - // we do not clear the fields, but leave the row unchanged. - // the DBF has the deleted flag. This emulates what DBF files do - - row:AcceptChanges() - endif - - else - if row:RowState.HasFlag(DataRowState.Added) - lOk := SELF:_ExecuteInsertStatement(row) - row:AcceptChanges() - elseif row:RowState.HasFlag(DataRowState.Modified) - lOk := SELF:_ExecuteUpdateStatement(row) - row:AcceptChanges() - endif - endif - catch e as Exception - lOk := false - self:_dbfError(ERDD.WRITE, XSharp.Gencode.EG_WRITE, "SqlRDD:GoCold", e:Message ) - end try + foreach var rowId in _updatedRecNos + lOk := self:_UpdateRow(rowId) if !lOk exit endif @@ -472,7 +415,7 @@ partial class SQLRDD inherit Workarea else self:DataTable:RejectChanges() endif - _updatedRowIds:Clear() + _updatedRecNos:Clear() self:_GetRecCount() endif return lOk @@ -653,7 +596,7 @@ partial class SQLRDD inherit Workarea LOCAL result AS LOGIC TRY VAR nRec := Convert.ToUInt32( oRec ) - if nRec != RowNumber + if oRec != self:CurrentRow[self:_recnoColumNo] result := SELF:GoTo( (DWORD) nRec ) endif CATCH ex AS Exception @@ -780,7 +723,6 @@ partial class SQLRDD inherit Workarea end try endif else - self:_rowNumber := self:RowNumber return super:Deleted endif end get @@ -934,9 +876,15 @@ partial class SQLRDD inherit Workarea end try // In ADS this happens in here: ACE.AdsUnlockRecord(SELF:_Table, dwRecno) - if (self:Deleted) + if self:Deleted self:_ExecuteDeleteStatement(CurrentRow) endif + if self:_updatedRecNos.Count > 0 + foreach var nRecNo in self:_updatedRecNos + self:_UpdateRow(nRecNo) + next + self:_updatedRecNos:Clear() + endif return true end method diff --git a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg index 43dc8e9a67..a6a326500f 100644 --- a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg +++ b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg @@ -39,7 +39,7 @@ partial class SQLRDD private _emptyValues as object[] private _updatableColumns as List private _keyColumns as List - private _updatedRowIds as List + private _updatedRecNos as List private _orderBagList as List private _rowNumber as long @@ -127,7 +127,7 @@ partial class SQLRDD SELF:_currentPageNo := 1 SELF:_firstPageNo := 1 self:_trimValues := true // trim String Valuess - SELF:_updatedRowIds := List{} + SELF:_updatedRecNos := List{} SELF:_keyColumns := List{} SELF:_updatableColumns := List{} SELF:_orderBagList := List{} @@ -607,26 +607,33 @@ partial class SQLRDD return result PRIVATE METHOD _GotoRecord(nRec as DWORD) AS LOGIC - if SELF:DataTable:Rows:Count < 1 - // Brute walk - SELF:_command:CommandText := _builder:BuildRowNumberStatement(nRec) - var result := SELF:_command:ExecuteScalar(SELF:_oTd:Name) - var iResult := Convert.ToInt64(result) - // shouldn't this be ToUInt32? - - // determine correct page - SELF:_currentPageNo := (INT) ((iResult - 1) / SELF:_oTd:PageSize) + 1 - SELF:_ClearTable() - SELF:DataTable := SELF:_ReadTable("") - end if - - // locate the row in the page - SELF:RowNumber := 1 - DO WHILE SELF:RowNumber <= SELF:DataTable:Rows:Count - IF SELF:RecNo == nRec + + // if record to go to is already in loaded page + if self:_GotoRecordInPage(nRec) + return true + endif + + // Brute walk + SELF:_command:CommandText := _builder:BuildRowNumberStatement(nRec) + var result := SELF:_command:ExecuteScalar(SELF:_oTd:Name) + var iResult := Convert.ToInt64(result) + + // determine correct page + SELF:_currentPageNo := (INT) ((iResult - 1) / SELF:_oTd:PageSize) + 1 + SELF:_ClearTable() + SELF:DataTable := SELF:_ReadTable("") + + RETURN self:_GotoRecordInPage(nRec) + + PRIVATE METHOD _GotoRecordInPage(nRec as DWORD) AS LOGIC + LOCAL nRowNumber AS INT + nRowNumber := 1 + DO WHILE nRowNumber <= SELF:DataTable:Rows:Count + IF nRec == Convert.ToInt32(SELF:DataTable:Rows[nRowNumber-1][self:_recnoColumNo]) + SELF:RowNumber := nRowNumber RETURN TRUE ENDIF - SELF:RowNumber+= 1 + nRowNumber += 1 ENDDO RETURN FALSE @@ -654,6 +661,71 @@ partial class SQLRDD SELF:_CheckEofBof() RETURN TRUE + PRIVATE METHOD _UpdateRow(nRecNo AS INT) AS LOGIC + local row as DataRow + local lOk := TRUE as logic + try + foreach tableRow as DataRow in self:DataTable:Rows + if (int)tableRow[self:_recnoColumNo] = nRecNo + row := tableRow + endif + next + + // Check row lock + var dbLockInfo := DbLockInfo{} + dbLockInfo:RecId := row[_oTd:RecnoColumn] + var myLock := false + var otherLock := false + SELF:CheckLock(dbLockInfo, StringBuilder{}, myLock, otherLock) + if otherLock + return false + endif + + if !myLock + SELF:Lock(ref dbLockInfo) + endif + + lOk := true + if super:Deleted + local wasNew := false as logic + // Append from may add deleted rows + if row:RowState.HasFlag(DataRowState.Added) + lOk := SELF:_ExecuteInsertStatement(row) + row:AcceptChanges() + wasNew := true + endif + if self:_deletedColumnNo > -1 + if !wasNew + // already written with _deletedColumnNo with the correct value + lOk := SELF:_ExecuteUpdateStatement(row) + if lOk + row:AcceptChanges() + endif + endif + else + lOk := SELF:_ExecuteDeleteStatement(row) + // we do not clear the fields, but leave the row unchanged. + // the DBF has the deleted flag. This emulates what DBF files do + + row:AcceptChanges() + endif + + else + if row:RowState.HasFlag(DataRowState.Added) + lOk := SELF:_ExecuteInsertStatement(row) + row:AcceptChanges() + elseif row:RowState.HasFlag(DataRowState.Modified) + lOk := SELF:_ExecuteUpdateStatement(row) + row:AcceptChanges() + endif + endif + catch e as Exception + lOk := false + self:_dbfError(ERDD.WRITE, XSharp.Gencode.EG_WRITE, "SqlRDD:GoCold", e:Message ) + end try + + RETURN lOk + PRIVATE METHOD LockRecNo(lockInfo ref DbLockInfo) AS INT var lockRecNo := 0 if lockInfo:Method != XSharp.RDD.Support.DbLockInfo.LockMethod.File From d084e311ded23ef3fa913d39c1606da0bb0e9d1e Mon Sep 17 00:00:00 2001 From: Thomas Stutz Date: Mon, 29 Jun 2026 08:31:23 +0200 Subject: [PATCH 3/3] - Small bugfixes - Remove xs_connection - Performance improvement for existing tables --- src/Runtime/XSharp.SQLRdd/Classes/Command.prg | 2 +- .../XSharp.SQLRdd/Classes/Connection.prg | 85 +++++-------------- .../XSharp.SQLRdd/RDD/SQLDbOrderBag.prg | 2 +- src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg | 12 ++- .../XSharp.SQLRdd/RDD/SQLRDD-Private.prg | 2 +- 5 files changed, 33 insertions(+), 70 deletions(-) diff --git a/src/Runtime/XSharp.SQLRdd/Classes/Command.prg b/src/Runtime/XSharp.SQLRdd/Classes/Command.prg index 9f39d8b4e2..3c589696fb 100644 --- a/src/Runtime/XSharp.SQLRdd/Classes/Command.prg +++ b/src/Runtime/XSharp.SQLRdd/Classes/Command.prg @@ -32,7 +32,7 @@ class SqlDbCommand inherit SqlDbHandleObject implements IDisposable /// The DBMS Provider class. property Provider as ISqlDbProvider get iif(Connection == null, null, Connection:Provider) /// The text of the Command object. - property CommandText as string get DbCommand:CommandText set DbCommand:CommandText := value + property CommandText as string get iif(DbCommand == null, "", DbCommand:CommandText) set DbCommand:CommandText := value #endregion diff --git a/src/Runtime/XSharp.SQLRdd/Classes/Connection.prg b/src/Runtime/XSharp.SQLRdd/Classes/Connection.prg index d34472caf7..f5fb262b7b 100644 --- a/src/Runtime/XSharp.SQLRdd/Classes/Connection.prg +++ b/src/Runtime/XSharp.SQLRdd/Classes/Connection.prg @@ -105,6 +105,8 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable internal const DefaultConnection := "DEFAULT" as string internal static Connections as List static internal property DefaultCached as logic auto + private static oExistingTables := List{} as List + static constructor() Connections := List{} DefaultCached := true @@ -273,10 +275,7 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable KeepOpen := DefaultCached if @@Callback != null self:CallBack += @@Callback - SELF:MetadataProvider := SqlMetadataProviderCallBack{SELF} - ELSE - SELF:MetadataProvider := SqlMetadataProviderIni{SELF} - endif + endif Connections:Add(self) _datasourceProperties := Dictionary{StringComparer.OrdinalIgnoreCase} _commands := List{} @@ -284,9 +283,7 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable self:ForceOpen() SELF:_FillMetadataCollections() SELF:_FillDataSourceProperties() - SELF:_CheckConnectionTable() SELF:_CreateLockTable() - SELF:_Login() SELF:InitializeLockTimer() // Todo: Check for # of open users and close the connection when no users are left and then throw an exception return @@ -303,9 +300,11 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable if self:RDDs:Count > 0 return false endif - self:_Logout() // Logout the workstation from the Open Connections table - _command:Dispose() + if _command != null + _command:Dispose() + endif + foreach var cmd in SELF:_commands:ToArray() if cmd:Connection == self cmd:Dispose() @@ -480,7 +479,7 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable /// Table name to display for Event Handler /// DbDataReader or NULL when an exception occurred method ExecuteReader(cCommand as string, cTable := __FUNCTION__ as STRING) as DbDataReader - local result := null as DbDataReader + local result := null as DbDataReader try _command:CommandText := cCommand result := _command:ExecuteReader(cTable) @@ -627,6 +626,9 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable /// TRUE when the table exists. method DoesTableExist(cTableName as string) as logic try + if oExistingTables:Contains(cTableName) + return true + endif if !SELF:HasCollection(TABLECOLLECTION) return false endif @@ -634,12 +636,16 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable local aTableRestrictions := string[]{4} as string[] aTableRestrictions[2] := cTableName var dt := self:DbConnection:GetSchema(TABLECOLLECTION, aTableRestrictions) - return dt:Rows:Count > 0 + if dt:Rows:Count > 0 + oExistingTables:Add(cTableName) + return true + endif else var dt := self:DbConnection:GetSchema(TABLECOLLECTION) foreach row as DataRow in dt:Rows var tbl := row["TABLE_NAME"]:ToString() if String.Compare(tbl, cTableName, true) == 0 + oExistingTables:Add(cTableName) return true endif next @@ -737,58 +743,6 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable end method #endregion - - - private method _CheckConnectionTable() as void - if SELF:DoesTableExist(CONNECTIONSTABLE) - return - endif - var sb := StringBuilder{} - sb:Append(SELF:Provider:CreateTableStatement) - using var cmd := SqlDbCommand{"ConnectionTable", self, false} - - sb:Replace(SqlDbProvider.TableNameMacro,CONNECTIONSTABLE) - sb:Replace(SqlDbProvider.FieldDefinitionListMacro, "station varchar(50), username varchar(50), lastlogin varchar(10), refcount int") - cmd:CommandText := sb:ToString() - cmd:ExecuteNonQuery("License") - - - private method _Login() as void - SELF:_LoginWorker(true) - private method _Logout() as void - SELF:_LoginWorker(false) - private method _LoginWorker(lIn as LOGIC) as void - var user := Environment.UserName - var station := Environment.MachineName - var dDate := DateTime.Now - var today := dDate:Year:ToString()+"-"+dDate:Month:ToString("0#")+"-"+dDate:Day:ToString("0#") - SELF:BeginTrans() - // clear old logins - using var cmd := SqlDbCommand{"Licenses", self, false} - cmd:CommandText := "Delete from "+CONNECTIONSTABLE+" where lastlogin < @p3" - cmd:ClearParameters() - cmd:AddParameter("@p1",user) - cmd:AddParameter("@p2",station) - cmd:AddParameter("@p3",today) - cmd:ExecuteNonQuery("License") - var sWhere := "where username = @p1 and station = @p2 and lastlogin = @p3" - if lIn - cmd:CommandText := "select count(*) from "+CONNECTIONSTABLE+" "+sWhere - var result := Convert.ToInt64(cmd:ExecuteScalar()) - if result == 0 - cmd:CommandText := "Insert into "+CONNECTIONSTABLE+"(username, station, lastlogin, refcount) values(@p1, @p2, @p3, 0)" - cmd:ExecuteNonQuery("Login") - endif - cmd:CommandText := "Update "+CONNECTIONSTABLE+" set refcount = refcount + 1 "+sWhere - cmd:ExecuteNonQuery("Login") - else - cmd:CommandText := "Update "+CONNECTIONSTABLE+" set refcount = refcount - 1 "+sWhere - cmd:ExecuteNonQuery("Logout") - cmd:CommandText := "delete from "+CONNECTIONSTABLE+" "+sWhere+" and refcount <= 0" - cmd:ExecuteNonQuery("Logout") - - endif - SELF:CommitTrans() #region Implement IDisposable /// public override method Dispose() as void @@ -798,7 +752,6 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable #endregion - INTERNAL CONST CONNECTIONSTABLE := "xs_connections" as string INTERNAL CONST DEFAULT_ALLOWUPDATES := TRUE AS LOGIC INTERNAL CONST DEFAULT_COMPAREMEMO := TRUE AS LOGIC INTERNAL CONST DEFAULT_DELETEDCOLUMN := "" AS STRING @@ -913,6 +866,9 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable // Refresh my Locks using var cmdRefresh := SqlDbCommand{"RefreshLockTable", SELF, false} + if !cmdRefresh:Connection:IsOpen + cmdRefresh:Connection:ForceOpen() + endif var updateStatement := SELF:Provider:UpdateStatement:Replace(SqlDbProvider.TableNameMacro, LockTableName) updateStatement := updateStatement:Replace(SqlDbProvider.ColumnsMacro, "lockdatetime = " + SELF:Provider:CurrentDateTime) updateStatement := updateStatement:Replace(SqlDbProvider.WhereMacro, "connectionid = " + parameterName1) @@ -923,6 +879,9 @@ class SqlDbConnection inherit SqlDbHandleObject implements IDisposable // Clear all old locks using var cmdClear := SqlDbCommand{"ClearLockTable", SELF, false} + if !cmdClear:Connection:IsOpen + cmdClear:Connection:ForceOpen() + endif cmdClear:CommandText := SELF:Provider:DeleteStatement:Replace(SqlDbProvider.TableNameMacro, LockTableName):Replace(SqlDbProvider.WhereMacro, "LockDateTime < " + parameterName1) cmdClear:Parameters := List{} cmdClear:Parameters:Add(SqlDbParameter{parameterName1, DateTime.Now.AddSeconds(-120)}) diff --git a/src/Runtime/XSharp.SQLRdd/RDD/SQLDbOrderBag.prg b/src/Runtime/XSharp.SQLRdd/RDD/SQLDbOrderBag.prg index 0bbb3eefe6..f0379efe92 100644 --- a/src/Runtime/XSharp.SQLRdd/RDD/SQLDbOrderBag.prg +++ b/src/Runtime/XSharp.SQLRdd/RDD/SQLDbOrderBag.prg @@ -58,7 +58,7 @@ internal class SqlDbOrderBag INHERIT BaseIndex super(oRdd) self:RDD := oRdd SELF:LogicalName := cName - if File(cName) + if System.IO.File.Exists(cName) cName := FPathName() SELF:Path := System.IO.Path.GetDirectoryName(cName) SELF:FileName := System.IO.Path.GetFileNameWithoutExtension(cName) diff --git a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg index 0590954b55..522c8f3d4b 100644 --- a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg +++ b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Main.prg @@ -148,7 +148,7 @@ partial class SQLRDD inherit Workarea throw Exception{} endif if !self:_oTd:HasRecnoColumn - throw Exception{"RecnoColumn is required"} + throw Exception{"RecnoColumn is required for table " + info:FileName} endif endif local aUpdatableColumns := null as HashSet @@ -430,12 +430,12 @@ partial class SQLRDD inherit Workarea override method Close() as logic local lOk as logic lOk := SELF:GoCold() + self:UnLock(0) // This method deletes the temporary file after the file is closed foreach var bag in self:OrderBagList bag:Close() next - self:UnLock(0) - _connection:UnregisterRdd(self) + _connection?:UnregisterRdd(self) lOk := super:Close() return lOk @@ -843,6 +843,10 @@ partial class SQLRDD inherit Workarea end method public override method UnLock(oRecId as object) as logic + if Connection?:Provider is null .or. !self:Connection:IsOpen + return false + endif + try var sb := StringBuilder{} var sbWhere := StringBuilder{} @@ -865,7 +869,7 @@ partial class SQLRDD inherit Workarea cmd:AddParameter(self:Provider:ParameterPrefix+"p1", Environment.MachineName ?? String.Empty) cmd:AddParameter(self:Provider:ParameterPrefix+"p2", Environment.UserName ?? String.Empty) cmd:AddParameter(self:Provider:ParameterPrefix+"p3", self:Connection:ConnectionId:ToString()) - cmd:AddParameter(self:Provider:ParameterPrefix+"p4", _oTd:RealName ?? String.Empty) + cmd:AddParameter(self:Provider:ParameterPrefix+"p4", _oTd?:RealName ?? super:Alias ?? String.Empty) cmd:AddParameter(self:Provider:ParameterPrefix+"p5", (int)super:Area) if oRecId != null .and. oRecId is int .and. ((int)oRecId) > 0 cmd:AddParameter(self:Provider:ParameterPrefix+"p6", (int)oRecId) diff --git a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg index a6a326500f..2df7f5b813 100644 --- a/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg +++ b/src/Runtime/XSharp.SQLRdd/RDD/SQLRDD-Private.prg @@ -59,7 +59,7 @@ partial class SQLRDD #region Properties internal property Connection as SqlDbConnection get _connection - internal property Provider as ISqlDbProvider get _connection:Provider + internal property Provider as ISqlDbProvider get _connection?:Provider internal property Command as SqlDbCommand get _command internal property OrderBagList as List get _orderBagList internal property CurrentPage as int => _currentPageNo