RecordSet to RowSet conversion = InvalidArgumentException

if rs.Column( "storedID" ) <> Nil then // <-- throws InvalidArgumentException when the column is Nil
  rows(k).Column("storedID").IntegerValue = rs.Column( "storedID" ).IntegerValue
else
  rows(k).Column("storedID").IntegerValue = rs.Column( "ID" ).IntegerValue
end if

This code used to work, using RecordSet. If the column is there, read its value; if not, use another value that we know exists.

Using RowSet, this throws a InvalidArgumentException when the column doesn’t exist.

Can someone please show me how this supposed to be done using RowSet? Thanks.

Surely its:

If rs <> Nil then
…
End If

Thanks but no. I didn’t show it above but rs has already been checked for Nil. It’s the COLUMN that’s Nil.

RowSet requires using try … catch blocks for everything, but I can’t see how a try … catch block is going to work for what needs to be done here. This is why I’m asking for help.

Does the Message property of the InvalidArgumentException provide any kind of information? This seems like a tool I would find useful.

It seems like this should work, but it doesn’t:

try
  if rs.Column( "storedID" ) <> Nil then
    rows(k).Column("storedID").IntegerValue = rs.Column( "storedID" ).IntegerValue
  end if
catch e as InvalidArgumentException
  rows(k).Column("storedID").IntegerValue = rs.Column( "ID" ).IntegerValue
end try

This is happening in a loop which may be checking a million records and the debugger breaks every time this happens, which is … just slightly annoying. Am I supposed to wrap this in #Pragma BreakOnExceptions False ?? I can’t imagine that would be the right way to do it.

The Message is: “No column with name storedID”, which is obvious, that’s why I’m checking it for Nil, to see if it exists. Totally absurd. There must be some other way I’m supposed to be checking if a column exists?

That kind of disappointingly makes it sound by design.

The docs say:

If the column name does not exist, an InvalidArgumentException is raised.

So you need to try catch

Try

  rows(k).Column("storedID").IntegerValue = rs.Column( "storedID" ).IntegerValue
  return

catch e as InvalidArgumentException

  rows(k).Column("storedID").IntegerValue = rs.Column( "ID" ).IntegerValue

End Try
1 Like

Okay, to get it not to break the debugger a million times, it looks like …

#Pragma BreakOnExceptions False
try
  rows(k).Column("storedID").IntegerValue = rs.Column( "storedID" ).IntegerValue
catch e as InvalidArgumentException
  rows(k).Column("storedID").IntegerValue = rs.Column( "ID" ).IntegerValue
end try
#Pragma BreakOnExceptions True

Is this really right? It sure looks like bad coding to me.

Seems to me that either the column exists for all rows, or for none. Perhaps you should check for its existance once before entering any loop, set a flag to say yes/no, then enter your loop.

1 Like

The thing is, what you’re doing (selecting unknown columns) is the exception, not the norm. I haven’t once needed to nil check a column in all my time. I haven’t seen this change in design come up here in the forum since API 2.0 launched 5 years ago.

Nil checking the column does seem like a fun trick to do, but I see the value in having an InvalidArgumentException that tells you what column is missing. A NilObjectException, especially when you’re not expecting a column to be missing, will be a difficult hunt with no clues.

It leads me to ask “Why are you selecting * from a table where you don’t know what you’re going to get?” - but that doesn’t make the design cleaner.

There speaks reason … this is old code and I was trying not to change anything, but you’re right.

dim useID as Boolean
#Pragma BreakOnExceptions False
try
  dim j as integer = rs.Column( "storedID" ).IntegerValue
catch e as InvalidArgumentException
  useID = true
end try
#Pragma BreakOnExceptions True

Then check the flag in the loop and read the value accordingly.

@Aaron_Hunt, if you’re refactoring, consider checking the columns of the RowSet directly. You’ve got ColumnCount, ColumnAt which returns a DatabaseColumn, which gives you access to the name, type and value.

4 Likes

Thanks for the tip, sounds like the right approach.

I can see why it seems a bit weird. The method is actually an Extends on rs as Rowset (used to be RecordSet), so the rows can be coming from many places, the origin isn’t known (isn’t passed) and the column may be there or not.

Change the last line to:

#Pragma BreakOnExceptions Default

Otherwise exception handling may not behave the way you expect elsewhere.

2 Likes

may be usefull… modify it for rowset

Public Function HasField(extends aRecordSet as RecordSet, aFieldName as String, dontCheckForNull as Boolean=False) As Boolean
  ' checks if the given RecordSet contains a field with the given name
  ' and that the field is not empty or null
  
  dim res as Boolean = False
  dim maxCount as Integer = aRecordSet.FieldCount
  
  for i as Integer=1 to maxCount
    If aRecordSet.IdxField(i).Name=aFieldName Then
      If Not dontCheckForNull Then
        if aRecordSet.IdxField(i).StringValue<>"" then
          res = True
          exit For
        End If
      Else
        res = True
        Exit For
      End If
    End If
  next
  
  Return res
  
End Function

1 Like

Thanks; updated throughout the project.

1 Like

Today I learned…

Great tip @Anthony_G_Cyphers ! Had no idea Default was an option for the BreakOnExceptions #Pragma directive. :exploding_head:

1 Like

Thanks! … Here’s my version for RowSet:

Public Function HasColumn(extends rs as RowSet, columnName as String) As Boolean
  ' checks if the given RowSet contains a column with the given name
  
  dim found as Boolean
  dim lastIndex as Integer = rs.ColumnCount - 1
  
  for i as integer = 0 to lastIndex
    if rs.ColumnAt( i ).Name = columnName then
      found = true
      exit for
    end if
  next
  
  return found
End Function

NOTE: I didn’t need / want the “check for null” part so removed it.

A good way to avoid the clumsy exception handling. Greg O had pointed me also in this direction. Allows me to do this instead:

dim useID as Boolean = not rs.HasColumn( "storedID" )

That’s much better code in my book.

Thanks everyone!