ADO.RecordCount equals - 1 problem - asp-classic

When ever I try to access the RecordCount property, I always get a return value of -1. Below is my sample code.
Set oConn = Server.CreateObject ("ADODB.Connection")
oConn.Open Application("strConnectstring")
Set rs = Server.CreateObject ("ADODB.Recordset")
rs.ActiveConnection = oConn
SQL = "Publications_PicoSearchListing"
set rs = oConn.execute(SQL)
I'm not sure if I'm doing forwardCursor or dynamic cursors, or if the provider even supports the RecordCount property. How do I check if the provider supports RecordCount property or if I'm using either forwardCursor or dynamic cursors.
Any help would be appreciated.
Thank You

Recordcount is not supported with the default forward-only cursor.
you must add extra parameters to the open command
rs.open sql,conn,1,1
That should let you have access to rs.recordcount.
But paging is best done by using the Recordset.GetRows() + Recordset.Move() method.
http://databases.aspfaq.com/database/how-do-i-page-through-a-recordset.html
(scroll down to the bold "Recordset.GetRows() + Recordset.Move()" this is fastest way without using stored procedures)

Please note: unless you move to the end of the recordset there is no guarantee that the RecordCount will have been populated. The standard pattern to to iterate over each row in the recordset using While Not rs.EOF. In all the VBA code I've ever written, I have never relied on checking rs.RecordCount
Rather than checking the cursor type, you can set it. For example:
Set conn=Server.CreateObject("ADODB.Connection")
conn.Provider="Microsoft.Jet.OLEDB.4.0"
conn.Open(Server.Mappath("northwind.mdb"))
set rs = Server.CreateObject("ADODB.recordset")
sql="SELECT * FROM Customers"
rs.CursorLocation = adUseClient
rs.CursorType = adOpenStatic
rs.LockType = adLockBatchOptimistic
rs.Open sql, conn
If all you want is the count, why not emit a "SELECT Count(*) From Publications_PicoSearchListing"
Of Interest?: Understanding ADO's Default Cursor Type
Another alternative to get the RecordCount is to execute:
rs.MoveLast
rs.MoveFirst
and then check the RecordCount, and even then I seem to remember some cursor types aren't guaranteed (but memory hazy on this).
Also note: Don't use the MoveLast/MoveFirst unless you really need to: this will be slow with a large recordset or a recordset drawn across a network. Instead use the Count(*) technique.

For paging you can use the recordset.PageSize and recordset.AbsolutePage like this
Set rs = Server.CreateObject("ADODB.Recordset")
' make recordset use adUSEclient ( client side cursor)'
rs.CursorLocation = 3
' make recordset use the adOpenStatic cursor ( scrollable )'
rs.CursorType = 3
rs.PageSize = RecordsPerPage
rs.Open sql, conn
' go to selected page'
if not rs.EOF and not rs.BOF then
rs.AbsolutePage = page_you_want_to_go
end if
you then have access to recordset.PageCount to know the number of pages returned..

Related

How can I use an ADODB.CommandObject with ADODB.RecordSet?

I am trying to make a Classic ASP/VBScript website more secure by making SQL statements parameterized.
I have the following function:
Function OpenUpdateableRS(strSQL)
Dim rs
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open strSQL, cnDZ, adOpenKeyset, adLockPessimistic, adCmdText
Set OpenUpdateableRS = rs
Set rs = Nothing
End Function
I intend to convert it to something like:
Function SecureOpenUpdateableRS(strSQL, strParam1, strParam2)
Dim rs
Dim cmdOB
Set cmdOB = Server.CreateObject("ADODB.CommandObject")
With cmdOB
.ActiveConnection = cnDZ
.CommandText = strSQL
.Parameters(0).value = strParam1
.Parameters(0).value = strParam2
End With
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open cmdOB.Execute, , adOpenKeyset, adLockPessimistic
Set SecureOpenUpdateableRS = rs
Set rs = Nothing
End Function
When I call the function with:
Set rs = SecureOpenUpdateableRS("SELECT CustID, LastActive, LoggedIn, SessionID FROM tblLogins WHERE EMail = ? AND PWord = ?", strEMail, strPassword)
I get a "500 - Internal Server Error" which is probably because I disabled debugging on the server.
Any ideas on how I could make the original function more secure without breaking it?
You'll have to create the parameters and append them to the command's parameter collection. Just assigning the values to (the same!) parameter can't possibly work. Google for a sample; perhaps this VB! sample will get you started.
Added:
I can think of two strategies to derive the parameter types:
If you pass the correct/maximally specified parameter values to the function you can map VarType(value) constants to parameter type constants
If you do a SELECT based on the fieldnames in the command text, you can map the recordset's field .Types to parameter type constants
It won't be trivial to get this right for all possible cases. I would pass pairs of value and desired type to the function.

Script to select, update, insert and delete from MySQL with asp.net (VB)?

I've just switched from classic ASP to .net and I always used the following to SELECT, INSERT, UPDATE and DELETE from my MySQL databases:
' Create db connection
Function dbConn()
Set objConn = Server.CreateObject("ADODB.Connection")
objConn.Open "driver=x;Server=x;Port=x;Option=x;Database=x;Uid=x;Pwd=x"
Set dbConn = objConn
End Function
' Store data in array
Function SQL(myCommand,strSQL)
Set objConn = dbConn()
If myCommand = 0 Then
Set objRS = objConn.Execute(strSQL)
If NOT objRS.EOF Then arrRS = objRS.GetRows Else arrRS = Null
Else
Set objRS = objConn.Execute(strSQL,,128)
End If
Set objRS = Nothing : Set objConn = Nothing
End Function
For example, to use SELECT I'd just go:
Call SQL(0,"SELECT * FROM Users")
And to display the data:
If IsArray(arrRS) Then
For i = 0 to UBound(arrRS,2)
Response.Write(arrRS(0,i) & ", " & arrRS(1,i))
Next
End If
And to insert, update or delete I'd use:
Call SQL(1,"DELETE FROM Users WHERE UserID = 1")
Does anyone know if this is possible with ASP.Net - VB?
Or is there an even handier solution?
Cheers.
Yes, you can certainly do that with VB.NET. VB.NET supports almost everything ASP classic and vbscript could do. ADO.NET supports almost everything ADO did.
... not that you'd want to do it.
I strongly suggest that you look into the pattern and practices that ASP.NET allows. The newer methods are much better than the old ones.

Simple query with ADO and Classic ASP

I want to simply retrieve a single record from a database in a classic ASP page. The code below basically works, but there are a couple problems I need help solving:
1) I want to see if a record was returned or not. result is not Nothing, so the redirect at the bottom is never performed. contact.RecordCount always returns -1, so I apparently can't use that either. Oddly, trying to access RecordCount outside the function throws an "Object doesn't support this property or method: 'RecordCount'" error.
2) I've read about disconnected queries and have seen examples where the connection and command are closed and/or set to Nothing at the end of the function. Is there a definitive best practice on what I should do?
3) Will using a parameterized query fully protect me from SQL injection, or do I need to manually remove dangerous words and characters?
function GetContactByUsername(username)
Dim conn, command, param, contact
set conn = server.CreateObject("adodb.connection")
conn.Open Application("DatabaseConnectionString")
Set command = Server.CreateObject("ADODB.COMMAND")
set command.ActiveConnection = conn
command.CommandType = adCmdText
command.CommandText = "Select * from MY_DATABASE.dbo.Contact where Username = ?"
Set param = command.CreateParameter ("Username", adVarWChar, adParamInput, 50)
param.value = username
command.Parameters.Append param
Set contact = Server.CreateObject("ADODB.RECORDSET")
contact.Open command
Response.Write contact.RecordCount '' always -1
set GetContactByPurlCode = contact
end function
dim result
result = GetContactByUsername(Request.QueryString("username"))
if result is Nothing then '' never true
Response.Redirect "/notfound.asp"
end if
FirstName = Trim(result("FirstName"))
LastName = Trim(result("LastName "))
1) To check for a lack of records, use rs.EOF, not "Is Nothing." The RecordSet object is always an object. It's just that sometimes it doesn't have any rows.
If you want to use RecordCount but are getting -1, then switch to a client-side cursor (adUseClient).
2) No definitive best-practice here, but I've personally always closed the Connection and Command, and have not had much in the way of performance problems. Connection objects are particularly precious, so close them as early as possible on high volume pages.
3) Yes, parameterizing your variable is perfect, unless you are calling a stored procedure that constructs a dynamic query.
By the way, you should avoid "SELECT *" as that will cause you to return more data than needed and is a maintenance problem waiting to happen.

ASP Classic Named Parameter in Paramaterized Query: Must declare the scalar variable

I'm trying to write a parameterized query in ASP Classic, and it's starting to feel like i'm beating my head against a wall. I'm getting the following error:
Must declare the scalar variable "#something".
I would swear that is what the hello line does, but maybe i'm missing something...
<% OPTION EXPLICIT %>
<!-- #include file="../common/adovbs.inc" -->
<%
Response.Buffer=false
dim conn,connectionString,cmd,sql,rs,parm
connectionString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Data Source=.\sqlexpress;Initial Catalog=stuff"
set conn = server.CreateObject("adodb.connection")
conn.Open(connectionString)
set cmd = server.CreateObject("adodb.command")
set cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
cmd.CommandText = "select #something"
cmd.NamedParameters = true
cmd.Prepared = true
set parm = cmd.CreateParameter("#something",advarchar,adParamInput,255,"Hello")
call cmd.Parameters.append(parm)
set rs = cmd.Execute
if not rs.eof then
Response.Write rs(0)
end if
%>
Here's some sample code from an MSDN Library article on preventing SQL injection attacks. I cannot find the original URL, but googling the title keywords (Preventing SQL Injections in ASP) should get you there quick enough. Hope this real-world example helps.
strCmd = "select title, description from books where author_name = ?"
Set objCommand.ActiveConnection = objConn
objCommand.CommandText = strCmd
objCommand.CommandType = adCmdText
Set param1 = objCommand.CreateParameter ("author", adWChar, adParamInput, 50)
param1.value = strAuthor
objCommand.Parameters.Append param1
Set objRS = objCommand.Execute()
See the following page on MSDN, near the bottom, referring specifically to named parameters.
MSDN example
ADO is going to expect question marks instead of actual parameter names in this case. Right now, the SQL "select #something" is not actually parameterized: it sees the "#something" as an (undeclared) SQL variable, not as a parameter. Change your CommandText line to this:
cmd.CommandText = "select ?"
And I think you will get the result you are looking for.
Good luck!
with server.createobject("adodb.command")
.activeConnection = application("connection_string")
.commandText = "update sometable set some_col=? where id=?"
.execute , array(some_value, the_id)
end with
I'm not sure what your query is intended to accomplish. I'm also not sure that parameters are allowed in the select list. MSDN used to have (many years ago, probably) a decent article on where parameters were allowed in a query, but I can't seem to find it now.
OTTOMH, your attempts to supply the parameter values to ADO look correct. Does your query execute if you do something like this?
SELECT 1 FROM sometable WHERE somefield = #something

ADO error "Current Recordset does not support updating. This may be a limitation of the provider, or of the selected locktype."

I am doing some calculation with the data set I take from my database. Null values give errors so I tried replacing null values with zeros(0). Here is the error I get,
ADODB.Recordset error '800a0cb3'
Current Recordset does not support
updating. This may be a limitation of
the provider, or of the selected
locktype.
Never seen it before. Here is my code.
If IsNull(objRevenueToday("REVENUE")) Then
objRevenueToday("REVENUE") = 0
End If
Your recordset appears to be read-only. There could be a number of reasons for this; you're reading a view that contains a Group By clause, you don't have permissions, etc.
Using the syntax Set Recordset = Command.Execute always opens a read only cursor. What you need to do is open the cursor using the Recordset object. The Source parameter of the Open method is your Command object. This allows you to set the desired location and locktype.
Dim cmdProc as ADODB.Command
Dim rsData as ADODB.Recordset
Set cmdProc = New ADODB.Command
With cmdProc
Set .ActiveConnection = SomeConnection
.CommandType = adCmdStoredProc
.CommandText = "selCustomer"
' ... Create parameters
End With
Set rsData as New ADODB.Recordset
rsData.Open cmdProc,, adOpenStatic,adLockBatchOptimistic
'...Process recordset data.
Here is the solution:
If IsNull(objRevenueToday("REVENUE")) Then
RevenueToday = "0"
Else
RevenueToday = objRevenueToday("REVENUE")
End If
Not very ideal but fixed my error.
Assuming SQL Server (although similar techniques available in other DBs.
Change the query so that is will not return nulls in records. For example in the T-SQL
SELECT ISNULL(REVENUE, 0), .... FROM ....
Change the settings as below. It force the client side cursor...It worked for me
set pagedlistrs=CreateObject("adodb.recordset")
pagedlistrs.cursorlocation = 3 ' adUseClientpagedlistrs
pagedlistrs.Open SQL, objConn, 3,3,1

Resources