I'm stumped on something which should be very straight-forward. I have a SQL Server database, and I'm trying to update a non-nullable varchar or nvarchar field with an empty string. I know it's possible, because an empty string '' is not the same thing as NULL. However, using the TADOQuery, it is not allowing me to do this.
I'm trying to update an existing record like so:
ADOQuery1.Edit;
ADOQuery1['NonNullFieldName']:= '';
//or
ADOQuery1.FieldByName('NonNullFieldName').AsString:= '';
ADOQuery1.Post; //<-- Exception raised while posting
If there is anything in the string, even just a single space, it saves just fine, as expected. But, if it is an empty string, it fails:
Non-nullable column cannot be updated to Null.
But it's not null. It's an empty string, which should work just fine. I swear I've passed empty strings many, many times in the past.
Why am I getting this error, and what should I do to resolve it?
Additional details:
Database: Microsoft SQL Server 2014 Express
Language: Delphi 10 Seattle Update 1
Database drivers: SQLOLEDB.1
Field being updated: nvarchar(MAX) NOT NULL
I can reproduce your reported problem using the code below with SS2014, the OLEDB driver and
Seattle and the difference in behaviour when the table has been created with MAX as the column size and a specific number (4096 in my case). I thought I would post this is as an alternative
answer because it not only shows how to investigate this difference systematically
but also identifies why this difference arises (and hence how to avoid it in future).
Please refer to and execute the code below, as written, i.e. with the UseMAX define
active.
Turning on "Use Debug DCUs" in the the project options before executing the code, immediately
reveals that the described exception occurs in Data.Win.ADODB at line 4920
Recordset.Fields[TField(FModifiedFields[I]).FieldNo-1].Value := Data
of TCustomADODataSet.InternalPost and the Debug evaluation window reveals that
Data at this point is Null.
Next, notice that
update jdtest set NonNullFieldName = ''
executes in an SSMS2014 Query window without complaint (Command(s) completed successfully.), so it seems that the
fact that Data is Null at line 4920 is what is causing the problem and the next question is "Why?"
Well, the first thing to notice is that the form's caption is displaying ftMemo
Next, comment out the UseMAX define, recompile and execute. Result: No exception
snd notice that the form's caption is now displaying ftString.
And that's the reason: Using a specific number for the column size means that
the table metadata retrieved by the RTL causes the client-side Field to be created
as a TStringField, whose value you can set by a string assignment statement.
OTOH, when you specify MAX, the resulting client-side Field is of type ftMemo,
which is one of Delphi's BLOB types and when you assign
string values to an ftMemo field, you are at the mercy of code in Data.DB.Pas , which does all the reading (and writing) to the record buffer using a TBlobStream. The problem with that is that as far as I can see, after a lot of experiments and tracing through the code, the way a TMemoField uses a BlobStream fails to properly distinguish between updating the field contents to '' and setting the field's value to Null (as in System.Variants).
In short, whenever you try to set a TMemoField's value to an empty string, what actually happens is that the field's state is set to Null, and this is what causes the exception in the q. AFAICS, this is unavoidable, so no work-around is obvious, to me at any rate.
I have not investigated whether the choice between ftMemo and ftString is made by the Delphi RTL code or the MDAC(Ado) layer it sits upon: I would expect it is actually determined by the RecordSet TAdoQuery uses.
QED. Notice that this systematic approach to debugging has revealed the
problem & cause with very little effort and zero trial and error, which was
what I was trying to suggest in my comments on the q.
Another point is that this problem could be tracked down entirely without
resorting to server-side tools including the SMSS profiler. There wasn't any need to use the profiler to inspect what the client was sending to the server
because there was no reason to suppose that the error returned by the server
was incorrect. That confirms what I said about starting investigation at the client side.
Also, using a table created on the fly using IfDefed Sql enabled the problem effectively to be isolated in a single step by simple observation of two runs of the app.
Code
uses [...] TypInfo;
[...]
implementation[...]
const
// The following consts are to create the table and insert a single row
//
// The difference between them is that scSqlSetUp1 specifies
// the size of the NonNullFieldName to 'MAX' whereas scSqlSetUp2 specifies a size of 4096
scSqlSetUp1 =
'CREATE TABLE [dbo].[JDTest]('#13#10
+ ' [ID] [int] NOT NULL primary key,'#13#10
+ ' [NonNullFieldName] VarChar(MAX) NOT NULL'#13#10
+ ') ON [PRIMARY]'#13#10
+ ';'#13#10
+ 'Insert JDTest (ID, [NonNullFieldName]) values (1, ''a'')'#13#10
+ ';'#13#10
+ 'SET ANSI_PADDING OFF'#13#10
+ ';';
scSqlSetUp2 =
'CREATE TABLE [dbo].[JDTest]('#13#10
+ ' [ID] [int] NOT NULL primary key,'#13#10
+ ' [NonNullFieldName] VarChar(4096) NOT NULL'#13#10
+ ') ON [PRIMARY]'#13#10
+ ';'#13#10
+ 'Insert JDTest (ID, [NonNullFieldName]) values (1, ''a'')'#13#10
+ ';'#13#10
+ 'SET ANSI_PADDING OFF'#13#10
+ ';';
scSqlDropTable = 'drop table [dbo].[jdtest]';
procedure TForm1.Test1;
var
AField : TField;
S : String;
begin
// Following creates the table. The define determines the size of the NonNullFieldName
{$define UseMAX}
{$ifdef UseMAX}
S := scSqlSetUp1;
{$else}
S := scSqlSetUp2;
{$endif}
ADOConnection1.Execute(S);
try
ADOQuery1.Open;
try
ADOQuery1.Edit;
// Get explicit reference to the NonNullFieldName
// field to make working with it and investigating it easier
AField := ADOQuery1.FieldByName('NonNullFieldName');
// The following, which requires the `TypInfo` unit in the `USES` list is to find out which exact type
// AField is. Answer: ftMemo, or ftString, depending on UseMAX.
// Of course, we could get this info by inspection in the IDE
// by creating persistent fields
S := GetEnumName(TypeInfo(TFieldType), Ord(AField.DataType));
Caption := S; // Displays `ftMemo` or `ftString`, of course
AField.AsString:= '';
ADOQuery1.Post; //<-- Exception raised while posting
finally
ADOQuery1.Close;
end;
finally
// Tidy up
ADOConnection1.Execute(scSqlDropTable);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Test1;
end;
The problem occurs when using MAX in the data type. Both varchar(MAX) and nvarchar(MAX) exploit this behavior. When removing MAX and replacing it with a large number, such as 5000, then it allows empty strings.
Related
I'm trying to creating a table in a SQL Server database using FireDAC. However, instead of using the index name I provide, FireDAC uses a bad index name, raising an exception and the table does not get created. Am I doing something wrong? If not, is there a work-around?
Note that I'm using the valid database schema name cnf for TableName. I specifically need to create the table in a schema.
Simplest test case:
var
Connection: TFDConnection;
Table: TFDTable;
begin
Connection := TFDConnection.Create(nil);
Table := TFDTable.Create(nil);
try
Connection.Params.Add ('DriverID=MSSQL');
Connection.Params.Add ('OSAuthent=No');
Connection.Params.Add ('User_Name=sa');
Connection.Params.Add ('Password=XXXXXX');
Connection.Params.Add ('Server=DAVE-DELL\MSSQLSERVER2016');
Connection.Params.Add ('Database=PROJECT_DB');
Connection.Params.Add ('MARS=No');
Connection.Open;
Table.Connection := Connection;
Table.TableName := 'cnf.TestTable';
Table.FieldDefs.Add ('TableID', ftAutoInc, 0, true);
Table.FieldDefs.Add ('Field1', ftInteger, 0, true);
Table.FieldDefs.Add ('Field2', ftstring, 100, true);
Table.IndexDefs.Add ('PK_XYZ', 'TableID', [ixPrimary]); // should use this index name!
Table.CreateTable (true);
finally
Table.Free;
Connection.Free;
end;
end;
An exception is raised:
[FireDAC][Phys][ODBC][Microsoft][SQL Server Native Client 11.0][SQL Server]Incorrect syntax near '.'.
Running SQL Server Profiler shows me that FireDAC is trying to create the index using the following SQL code:
ALTER TABLE temp.TestTable ADD CONSTRAINT [cnf].[PK_TestTable] PRIMARY KEY (TableID)
And, of course, [cnf].[PK_TestTable] is not a valid index name in T-SQL, which is the crux of the problem.
If I remove the line Table.IndexDefs.Add, the table is created properly, but without the index.
If I replace that line with the following, it gives the same problem:
with Table.IndexDefs.AddIndexDef do begin
Name := 'PK_XYZ';
Options := [ixPrimary];
Fields := 'TableID';
end;
If I replace setting the table name with the following, it gives the same problem:
Table.TableName := 'TestTable';
Table.SchemaName := 'cnf';
Why is it using it's own (wrong) index name, instead of the name I gave it? (i.e. PK_XYZ)
Embarcadero® Delphi 10.1 Berlin Version 24.0.25048.9432
SQL Server 2016 (SP2-CU4) - 13.0.5233.0 (X64)
Am I doing something wrong?
Why is it using it's own (wrong) index name, instead of the name I gave it?
You seem to be doing everything just right. The issue is with the generated SQL command as you have tracked that down. SQL Server doesn't allow schema name in constraint name when adding a constraint using ALTER TABLE. Constraints created this way automatically become part of schema of the related table, however you should later use schema name when referring to the constraint:
SELECT OBJECT_ID('cnf.PK_XYZ')
Now where do the things go wrong? FireDAC uses TFDPhysCommandGenerator and its ancestors to generate SQL commands for specific DBMS. Your call to CreateTable method results in call to TFDPhysCommandGenerator.GetCreatePrimaryKey, which is responsible for generating SQL for primary key. It also contains this code:
sTab := GetFrom;
FConnMeta.DecodeObjName(sTab, rName, nil, [doUnquote]);
rName.FObject := 'PK_' + rName.FObject;
Result := 'ALTER TABLE ' + sTab + ' ADD CONSTRAINT ' +
FConnMeta.EncodeObjName(rName, nil, [eoQuote, eoNormalize]) + ' PRIMARY KEY (';
What this code does is that it takes your fully qualified table name (sTab) splits it (DecodeObjName) into parts (rName) prepends 'PK_' to table name and joins the parts (EncodeObjName) back to fully qualified name, which is then used as the constraint name for your primary key. Now we can clearly see that command generator ignores your index name and generates erroneous T-SQL. This can either be a bug or just a not supported feature. EMBT has to make decision on that. I'd recommend reporting it as a bug.
Is there a work-around?
Yes, you can either hook problematic method or you can override it in your own derived class. Implementation none of these is trivial and due to legal issues I'm not going to extend it here, because I would have to duplicate the original FireDAC code.
As for the syntax error adding these lines to 'TFDPhysCommandGenerator.GetCreatePrimaryKey' implementation after DecodeObjName would fix the issue:
rName.FCatalog := '';
rName.FSchema := '';
rName.FBaseObject := '';
rName.FLink := '';
Fixing constraint name is going to be more cumbersome than that, because the method only receives index column names as argument and has no obvious access to original IndexDefs where you could just use index name as primary key constraint name. Gaining access to index name from there would also allow you to get rid of decoding/encoding table name into index name. This process, however, could be essential for other DMBS's than SQL Server.
PS: If only half of all the questions were written in this manner ... Thank you for this wonderful question.
I'm getting the error
The data type for parameter or SQL variable "SHAPE" is not supported in the routine, compound SQL statement, or parameter list of a cursor value constructor.. SQLCODE=-789, SQLSTATE=429BB, DRIVER=4.18.60
When I use the code
CREATE OR REPLACE TRIGGER z
BEFORE INSERT OR UPDATE ON x
REFERENCING NEW AS N
FOR EACH ROW
BEGIN
DECLARE contador INTEGER;
SET contador = (SELECT COUNT(*) FROM y s
WHERE NOT(n.yID = s.yID AND db2gse.ST_Contains(s.shape, n.shape) = 1));
IF contador > 0 THEN
SIGNAL SQLSTATE '20001' SET MESSAGE_TEXT ='invalid relationship';
END IF;
END#
But if take out the function ST_Contains, it's works, I don't have a clue why that happens.
EDIT: Windows 7 64bit Ultimate with DB2 11.1 Server edition with ibm data studio, sorry I forgot about this detail.
Well, the error message does give you a clue: structured types (which ST_Geometry and its children are) are not supported in compound SQL statements, such as your trigger body. You'll need to do this check before you convert data from the exchange format to geometry or store the exchange format in the table along with geometry.
In the latter case you would have, along with the shape ST_Geometry column another one, e.g. shape_wkt varchar(5000) and in the trigger do type conversion on the fly:
...AND db2gse.ST_Contains(
db2gse.ST_Geometry(s.shape_wkt),
db2gse.ST_Geometry(n.shape_wkt)
)
db2gse.ST_Geometry(s.shape_wkt) might be optional, because the value does not need to be instantiated in the trigger, so this might work:
...AND db2gse.ST_Contains(
s.shape,
db2gse.ST_Geometry(n.shape_wkt)
)
I want to add a new parameter to an existing stored procedure. Body of this procedure may have been already customized by users so I can't drop and recreate it. I don't need to modify the body, just the signature.
So I thought to do a replacement of the last existing parameter by itself + the new parameter.
replace(OBJECT_DEFINITION (OBJECT_ID(id)),'#last_param varchar(max)=null','#last_param varchar(max)=null, #new_param varchar(max)=null')
It works fine if the following string is found
#last_param varchar(max)=null
but doesn't work if there is spaces in the string.
I would like to use a regex to be sure it works in all cases but I'm not sure it's possible in SQL Server.
Can you help me please ?
Thanks
SQL Server does not natively support regular expressions. You'll have to look at more manual string-analyzing with the available string functions. Something like this:
set #obDef = OBJECT_DEFINITION(OBJECT_ID(id))
set #startLastParam = PATINDEX('%#last_param%varchar%(%max%)%=%null%', #obDef)
if #startLastParam = 0 begin
-- handle lastParam not found
end else begin
set #endLastParam = CHARINDEX('null', #obDef, #startLastParam) + 4 -- 4 = len('null')
set #newDef = STUFF(#obDef, #endLastParam, 0, ', #new_param varchar(max)=null')
end
This isn't very fool-proof/safe though. PATINDEX() only gives you the same % wildcard you know from LIKE, it may match no character, it may match half the stored proc to find the word max somewhere entirely outside the signature.
So don't just run this in your customers production ;) but if you are certain about the current stored proc signature, this might just do the trick for you.
I am using a function I read about here to merge a series of recordsets that are generated by a stored procedure that is called in a loop. I have used this function before in other stored procedure cases and it has never produced this problem.
The research I did online points to basically one of two reasons:
1) I am trying to update or insert a date/time value that is not formatted correctly into a SQL Server 2005 table
2) I am trying to insert a, for example, CHAR(60) string into a CHAR(50) field.
Reason one is not applicable here since I am not using dates (in datetime format at least).
Reason two seemed to be the most likely issue. So I did a series of Response.Write() to spit out the objField.Name, objField.Type, objField.ActualSize, and if it was a numeric field the objField.Precision and objField.NumericScale.
Let us say that the stored SQL procedure is called twice as I am querying for values that are occurring in the same time frame but 2 different states. The loop I have in the ASP page does a For Each on the state in the state list and calls the stored procedure for each of the elements in the state list. I then call the MergeRecordSets function so it combines the 2 results into one. The general rule is that the data types and sizes of the columns in each resultset must be the same. With my Response.Write() checks of each of the columns returned in the 2 data sets I have found that they are identical. Doing my checks I also found that it breaks on the first column that is a NUMERIC column. The previous columns it was OK with were all CHAR or VARCHAR.
Is there any other reason why this error would come up?
The following is how I am calling the record merger function. The oQueryResult is going to be the final output (the combined records). objSingleRS is the result set returned by the stored procedure.
If oQueryResult Is Nothing Then
Set oQueryResult = objSingleRS
Else
Set oQueryResult = MergeRecordSets(Array(oQueryResult, objSingleRS))
End If
Here is the merge function. The line in which the code breaks is marked below.
Function MergeRecordSets(arrRecordsets)
Dim x, y, objCurrentRS
Dim objMergedRecordSet, objField, blnExists
Set objMergedRecordSet = Server.CreateObject("ADODB.Recordset")
For x=0 To UBound(arrRecordsets)
Set objCurrentRS = arrRecordsets(x)
For Each objField In objCurrentRS.Fields
blnExists = False
For y=0 To objMergedRecordSet.Fields.Count-1
If LCase(objMergedRecordSet.Fields(y).Name) = Lcase(objField.Name) Then
blnExists = True : Exit For
End If
Next
If Not(blnExists) Then
objMergedRecordSet.Fields.Append objField.Name, objField.Type, objField.DefinedSize
'objMergedRecordSet.Fields(objMergedRecordset.Fields.Count-1).Attributes = 32 'adFldIsNullable
End If
Next
Next
objMergedRecordSet.Open
For x=0 To UBound(arrRecordsets)
Set objCurrentRS = arrRecordsets(x)
Do Until objCurrentRS.EOF
objMergedRecordSet.AddNew
For Each objField In objCurrentRS.Fields
If Not(IsNull(objField.Value)) Then
'Response.Write(objField.Name & "<br>")
'Response.Write(objField.Type & "<br>")
objMergedRecordSet.Fields(objField.Name).Value = objField.Value 'Here is where it throws the Error.
End If
Next
objCurrentRS.MoveNext
Loop
Next
objMergedRecordSet.MoveFirst
Set MergeRecordSets = objMergedRecordSet
End Function
Here is the full error message returned:
Microsoft Cursor Engine error '80040e21'
Multiple-step operation generated errors. Check each status value.
/includes/funcs/Common.asp, line 4109
You mentioned that you have numeric columns, but you never set the Precision and NumericScale properties when you create the new Field in objMergedRecordSet. You need to set these properties for adNumeric and adDecimal fields.
objMergedRecordSet.Fields.Append objField.Name, objField.Type, objField.DefinedSize
With objMergedRecordSet.Fields(objField.Name)
.Precision = objField.Precision
.NumericScale = objField.NumericScale
End With
Also make sure you are not trying to put a NULL into a column that will not accept a NULL value. There is also the possibility of a type mismatch to cause this error so make sure you are passing a numeric value.
- Freddo
I'm trying to insert a record into a table in a 3-tier database setup, and the middle-tier server generates the error message above as an OLE exception when it tries to add the first parameter to the query.
I've Googled this error, and I find the same result consistently: it comes from having a colon in a string somewhere in your query, which b0rks ADO's SQL parser. This is not the case here. There are no spurious colons anywhere. I've checked and rechecked the object definition against the schema for the table I'm trying to insert into. Everything checks out, and this has my coworkers stumped. Does anyone know what else could be causing this? I'm at my wits' end here.
I'm using Delphi 2007 and SQL Server 2005.
I can get this error, using Delphi 2007 and MSSQL Server 2008, and I found a workaround. (which is pretty crappy IMHO, but maybe its useful to you if yours is caused by the same thing.)
code to produce the error:
with TADOQuery.Create(nil)
do try
Connection := ADOConnection;
SQL.Text := ' (SELECT * FROM Stock WHERE InvCode = :InvCode ) '
+' (SELECT * FROM Stock WHERE InvCode = :InvCode ) ';
Prepared := true;
Parameters.ParamByName('InvCode').Value := 1;
Open; // <<<<< I get the "parameter object is...etc. error here.
finally
Free;
end;
I found two ways to fix it:
1) remove the brackets from the SQL, ie:
SQL.Text := ' SELECT * FROM Stock WHERE InvCode = :InvCode '
+' SELECT * FROM Stock WHERE InvCode = :InvCode ';
2) use two parameters instead of one:
with TADOQuery.Create(nil)
do try
Connection := ADOConnection;
SQL.Text := ' (SELECT * FROM Stock WHERE InvCode = :InvCode1 ) '
+' (SELECT * FROM Stock WHERE InvCode = :InvCode2 ) ';
Prepared := true;
Parameters.ParamByName('InvCode1').Value := 1;
Parameters.ParamByName('InvCode2').Value := 1;
Open; // <<<<< no error now.
finally
Free;
end;
I found this thread while searching the previously mentioned Exception message. In my case, the cause was an attempt to embed a SQL comment /* foo */ into my query.sql.text.
(I thought it would have been handy to see a comment go floating past in my profiler window.)
Anyhow - Delphi7 hated that one.
Here a late reply. In my case it was something completely different.
I tried to add a stored procedure to the database.
Query.SQL.Text :=
'create procedure [dbo].[test]' + #13#10 +
'#param int ' + #13#10 +
'as' + #13#10 +
'-- For the parameter you can pick two values:' + #13#10 +
'-- 1: Value one' + #13#10 +
'-- 2: Value two';
When I removed the colon (:) it worked. As it saw the colon as a parameter.
I just encountered this error myself. I'm using Delphi 7 to write to a 2003 MS Access database using a TAdoQuery component. (old code) My query worked fine directly in MS Access, but fails in Delphi through the TAdoQuery object. My error came from a colon (apologies to the original poster) from a date/time value.
As I understand it, Jet SQL date/time format is #mm/dd/yyyy hh:nn:ss# (0 left-padding is not required).
If the TAdoQuery.ParamCheck property is True then this format fails. (Thank you posters!) Two work-arounds are: a) set ParamCheck to False, or b) use a different date/time format, namely "mm/dd/yyyy hh:nn:ss" (WITH the double quotes).
I tested both of these options and they both worked.
Even though that double-quoted date/time format isn't the Jet date/time format, Access is pretty good at being flexible on these date/time formats. I also suspect it has something to do with the BDE/LocalSQL/Paradox (Delphi 7's native SQL and database engine) date/time format (uses double quotes, as above). The parser is probably designed to ignore quoted strings (double quotes are the string value delimiter in BDE LocalSQL), but may stumble somewhat on other non-native date/time formats.
SQL Server uses single quotes to delimit strings, so that might work instead of double quotes when writing to SQL Server tables (not tested). Or maybe the Delphi TAdoQuery object will still stumble. Turning off ParamCheck in that case may be the only option. If you plan to toggle the ParamCheck property value in code, you'll save some processing time by ensuring the SQL property is empty before enabling it, if you're not planning on parsing the current SQL.
I'm facing the same error described in your question. I've traced the error into ADODB.pas -> procedure TParameters.AppendParameters; ParameterCollection.Append(Items[I].ParameterObject).
By using breakpoints, the error was raised, in my case, by a parameter which should fill a DateTime field in the database and I've never filled up the parameter. Setting up the parameter().value:='' resolved the issue (I've tried also with varNull, but there is a problem - instead of sending Null in the database, query is sending 1 - the integer value of varNull).
PS: I know is a 'late late late' answer, but maybe somebody will reach at the same error.
If I remember well, you have to explicit put NULL value to the parameter. If you are using a TAdoStoredProc component, you should do this in design time.
Are you using any threading? I seem to remember getting this error when a timer event started a query while the ADO connection was being used for another synchronous query. (The timer was checking a "system available" flag every minute).
Have you set the DataType of the parameter or did you leave it as ftUnknown?
I have also had the same problem, but with a dynamic command (e.g. an Update statement).
Some of the parameters could be NULL.
The only way i could get it working, was setting the parameter.DataType := ftString and parameter.Size := 1 and not setting the value.
cmdUpdate := TADOCommand.Create(Self);
try
cmdUpdate.Connection := '**Conections String**';
cmdUpdate.CommandText := 'UPDATE xx SET yy = :Param1 WHERE zz = :Param2';
cmdUpdate.Parameters.ParamByName('Param2').Value := WhereClause;
if VarIsNull(SetValue) then
begin
cmdUpdate.Parameters.ParamByName('Param1').DataType := ftString;
cmdUpdate.Parameters.ParamByName('Param1').Size := 1;
end else cmdUpdate.Parameters.ParamByName('Param1').Value := SetValue;
cmdUpdate.Execute;
finally
cmdUpdate.Free;
end;
I just ran into this error today on a TADOQuery which has ParamCheck := False and has no colons in the SQL.
Somehow passing the OLECMDEXECOPT_DODEFAULT parameter to TWebBrowser.ExecWB() was causing this for me:
This shows the problem:
pvaIn := EmptyParam;
pvaOut := EmptyParam;
TWebBrowser1.ExecWB(OLECMDID_COPY, OLECMDEXECOPT_DODEFAULT, pvaIn, pvaOut);
This does not show the problem:
pvaIn := EmptyParam;
pvaOut := EmptyParam;
TWebBrowser1.ExecWB(OLECMDID_COPY, OLECMDEXECOPT_DONTPROMPTUSER, pvaIn, pvaOut);
A single double quote in the query can also raise this error from what I just experienced and I am not using parameters at all ...
You can get this error when attempting to use a time value in the SQL and forget to wrap it with QuotedStr().
I got the same error. Turned out, that it is because a parameter of the stored procedure was declared as varchar(max). Made it varchar(4000) and error disappeared.