Handling Tables, preventing dataset errors - database

I'm creating an application that requires me to post a lot of data into tables as well as changing values within the table.
Whenever I write applications most of my time seems to be stopping errors such as:
'Cannot perform this method on a closed dataset'`
or
'tblName not in edit or insert mode'`
I seemingly get these every time and they aren't easy to debug and find out what's going wrong where.
I'm just looking for some guidance on how I can stop this from happening, I know it's something I am doing. For example, at the moment I am getting:
'Cannot perform this operation on a closed dataset'
somewhere within this code:
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if not tblSystem.Active then
tblSystem.Open;
if not tblSpecs.Active then
tblSpecs.Open;
tblSpecs.Edit;
tblSpecs.Post;
tblSpecs.Close;
// Post changes to the notes on exit
tblSystem.Edit;
tblsystem.Post;
tblSystem.Close;
end;
and
procedure TfrmMain.chkAutoUpdateClick(Sender: TObject);
begin
tblSystem.Edit;
if chkAutoUpdate.Checked then
chkInstUpdates.Enabled := True
else
begin
chkInstUpdates.Enabled := False;
tblSystemAutoUpdate.AsBoolean := False;
end;
end;
I'm going to assume right away that this is bad code for pushing/pulling data from a table, any help would be appreciated. Also any debugging help would be amazing.

If I were to guess, I'd say that it's this line:
tblSystemAutoUpdate.AsBoolean := False;
You're trying to edit the value of a dataset field without its dataset actually being open.
WRT debugging help, when you get the exception message and it asks you to Break or Continue, choose Break. The stack trace view (upper-left pane in the standard layout) will show you the function call stack of the current thread, and that should be able to lead you right to it. Also, make sure you build developer builds of your code with the "Use Debug DCUs" project option enabled. That will let you trace into RTL and VCL units in the debugger.

Related

Edit/Insert mode error: Directly after tblbookings.edit

I'm working on my end year project for high school and this problem came up. This code is supposed to filter the database and only work on the data whose ID is bigger than intGroep2 (which is at this point 22). Then it must change the first row's 'pop' column to the value of cb501pop1.checked.
tblbookings.Open;
tblbookings.Edit;
tblbookings['pop'].edit;
tblbookings.filter := 'ID >= '+ inttostr(intGroep2);
tblbookings.filtered := true;
tblbookings.First;
tblbookings['pop'] := cb501pop1.Checked;
The problem arises with the following line:
tblbookings['pop'].edit;
If it is not there, the program gives me an error: 'dataset not in edit or insert mode.' And if it is there it gives me an 'invalid variant operation' error. One other question said to change "edit" to append, but it still gave the same error. Another question suggested that somebody posted already, which I didn't. Nowhere in my code is 'post'. This is supposed to be an easy answer and thus should only be practicing the basics. 'Afterscroll' and other stuff mentioned in other questions isn't supposed to be in the solution since we didn't learn it yet.
Thank you.
EDIT: tblbookings.fieldbyname('pop').edit; gives an error: missing operator.

AppleScript not looping all numbers when using repeat

I am using this script to close all "Alerts" in my notification bar:
tell application "System Events"
tell process "NotificationCenter"
set numwins to (count windows)
repeat with i from numwins to 1 by -1
click button "Close" of window i
end repeat
end tell
end tell
However this doesn't close them all, even when there are no "Alert without Close button".
try catch didn't help. What's wrong?
I tested your script and it seemed to run correctly on my machine, but there is always a potential for problems when you use a repeat loop on a mutating list: in other words, a list that changes as the repeat loop progresses. Each time you close a window Notification Center changes its window list and updates the properties of the remaining windows; the script can simply lose track. I'm a little surprised it doesn't throw errors when this happens, but...
You can try this code and see if it works. rather than referring to windows by index it repeatedly tries to close the last window, ignoring any errors, and keeps on until the window count is zero or it loops 100 times (that last is to prevent an endless loop in case something goes wrong).
tell application "System Events"
tell process "NotificationCenter"
repeat 100 times
try
tell last window
click button "Close"
end tell
end try
if (count of its windows) = 0 then
exit repeat
end if
end repeat
end tell
end tell
EDIT
Per comments, the above doesn't work quite as advertised, so let's switch it over to AppleScriptObjC:
use framework "Foundation"
property NSUserNotificationCenter : class "NSUserNotificationCenter"
NSUserNotificationCenter's defaultUserNotificationCenter's removeAllDeliveredNotifications()
This seems to do the trick on my machine. Of course, NSUserNotificationCenter is deprecated as of 10.14, so this won't work forever — eventually you'll have to shift over to the notification's framework — but it should work for a few more OS versions.
EDIT 2
Per another comment, anyone working on os 10.14 or later (Mojave and Catalina, to date) can do an equivalent AppleScriptObjC routine using the UserNotifications framework, like so:
use framework "UserNotifications"
set notificationCenter to class "UNUserNotificationCenter"'s currentNotificationCenter
notificationCenter's removeAllDeliveredNotifications()
Note that I've used a slightly different syntax (merely calling class "UNUserNotificationCenter" rather than setting up a property). Both work; the property syntax is only preferable when you need to pass the class to handlers.

In VB how do you display all strings in an array on a timer?

Ok, here's my use case. I have a form that pulls different parts of the form from a number of tables in a database. Each time I pull the info, I test to make sure there were no errors. Currently I'm using msgbox to show if there are errors, but I'm rewriting the app to instead display these messages in a "status" line area. It's just a label that I write the strings into on the form that flashes the error. This is working great in my single "You did this, and you got an error" forms. But in one of my forms it's about 6 things that could give errors, and it's on load. So it's not a single action, single error type deal.
My idea is to load any errors into an array, then display them one at a time for like 5 seconds each, then loop back and do it again, for at least 5 times. So in effect I want it to show each error, for 5 seconds, then show the next one for 5 seconds, then start over again, for 5 times, then stop.
Problem is, while I've got about 10 ideas on ways to maybe do this, they all seem really complicated and complex (and I don't want to spend 10 hours writing it only to figure out it's not going to work). I'm looking for the "best" way.
So.... Is an array even the best way? Or would a list or some other way be better? I'm looking for ideas here. How would YOU do it?
Eventually, you can use arrays for what you are trying to achieve. This could be done by following the steps :
When you are pulling the information from the tables, check for errors and if there is any, put it into an array.
Define a for loop which is going to loop 5 times
Define another nested loop to loop through the array to display the messages one by one
After each message display, put a sleep or delay of 5 seconds
If you follow these instructions, it just a matter of minutes into writing the codes
I ended up solving this by using a combobox and a list, then just having my combobox blink. On forms that can only throw one error, I use a label instead. Here is the lion's share of my code to do this:
Private Sub ErrorCheck()
If errorList.Count = 1 Then
ErrorComboBox.Visible = True
DismissErrorButton.Visible = True
For Each errors As String In errorList
ErrorComboBox.Text = (errors)
Next
ErrorBlinkTimer.Enabled = True
ElseIf errorList.Count > 1 Then
ErrorComboBox.Visible = True
DismissErrorButton.Visible = True
ErrorComboBox.Text = "There were errors, click drop down to view!"
For Each errors As String In errorList
ErrorComboBox.Items.Add(errors)
Next
Else
End If
End Sub
What I do is after each thing in a SUB that can throw errors, I have it collect the error and write it to errorList with errorList.add like so:
Catch ex As Exception
Dim errorStr = "An Error Occurred in " & Me.Name & " writing into the OpenEvents table " _
& "for setting the Alert radio button."
log(errorStr & " - " & ex.Message)
errorList.Add(errorStr)
the log function is a custom function for writing off to the log, but the rest is standard VB.net stuff. As you can see, if I get any exception (I do the same thing with each query too) it writes the error off to the errorList, then flashes the combobox with the error(s). I have some more logic to make it all nice and pretty, but you get the idea.
at the end of each sub that CAN throw errors, I just have it call:
ErrorCheck()
and like magic, if there were any errors a combobox list appears with bright red flashing text to let them know. If they click it, and there is more than one error, they can see them all in a nice list. Clicking it also makes it stop flashing, cause that would be annoying... LOL
This ended up working much better overall really, but it's not quite as pretty as my first way of doing it... but, no matter what I did, it caused issues on the form(s) to have it running through the flashing loop 5x's to display each error one after the other. I had found work arounds for most of it, but there was still a performance impact, so I dumped it for this. And it does the job well enough.

How to see the Generic Array's name while debugging?

I started using a lot of generics but now I find it harder and harder to debug, to know which array is actually begin worked on. See example:
Type
TData = record
DataID:integer;
DataName:string;
end;
var DataArr1,DataArr2,DataArr3:TArray<TData>;
procedure WorkOnData(Data:TArray<TData>);
begin
if Data = DataArr1 then // <-- PARKING HERE ON DEBUG I CAN SEE ARRAY DATA, BUT NOT WHICH ARRAY IT IS
ProcessA(DataArr1)
else if Data = DataArr2 then
ProcessB(DataArr2)
else if Data = DataArr3 then
ProcessC(DataArr3);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if Sender = Button1 then
WorkOnData(DataArr1)
else if Sender = Button2 then
WorkOnData(DataArr2)
else if Sender = Button3 then
WorkOnData(DataArr3);
end;
So, I can identify the array by comparison to get True/False:
Data = DataArr1
but, this doesn't give me info which array it is, before the comparison. So, I would need to put breakpoints after each comparison to know which one is True.
These obviously don't work:
Data.Name
TArray<Data>.Name
Is there any other way to know which array has been passed that I can see in debugger (Watch)?
Answer/Solution:
For anybody who faces the same problem, question: As Remy says in accepted answer that what I would like to achieve, is not possible. OK, now the quick workaround is to put the comparison (Data = DataArr1) into Watch and see which one resolves to True. Not best, but still usable as now we can see which array is actually being used.
There are no variable names once the code gets compiled. When you debug WorkOnData(), the only variable name it can display is Data, and there is no way for the debugger to know what Data is pointing at without evaluating an expression that you provide. So no, what you are asking for is basically not possible.
What you would likely have to do is wrap your array inside of another record that has a Name string field, and then pass that record around as needed. When you inspect it in the debugger, you would see its Name value.

Call to SetLength returns nil

There is a procedure looking like this:
procedure blabla;
var buffer: array of byte;
begin
Setlength(buffer, 10);
Setlength(buffer, someinteger);
end
after both calls buffer is still nil <- this is my problem
I normally consider myself an experienced programmer and i use this fundamental method on various other occasions. This is driving me nuts.
Did anyone of you have similar issues in the past?
If so, what was the problem?
My code is somewhat spaghetti because i had changed any line that seemed suspicious, but here is the full code:
full procedure
#Edit:
i have this code in another part of the same project:
procedure interleaveVertexes;
var
interleavedArray: array of TVec3Coord2;
begin
SetLength(interleavedArray, vertexcount);
end;
and it works.. like it should
i was using gdb and the lazarus ide to debug and apparently..
Both of them dont like variables called 'buffer' or 'Data'.
I understand the lazarus ide internally is using gdb anyway.
Even a variable is just named 'h' wouldn't let itself be inspected properly.
i just renamed them to 'buffa' and 'howdy' and now it seems to be working.
Got there by doing old-fashioned print-debugging and storing the pointer of my array in a cardinal variable. There they were fine. (Apart from the actual content of the array)
annoingly funny

Resources