Calling procedure with updates and a ResultSet in MyBatis - sql-server

I want to map a SQL Server stored procedure with MyBatis, using annotations.
#Select(value = "{call sp_cen_obliczcene(" +
"#{wytworId, mode=IN, jdbcType=NUMERIC}, " +
"#{rodzajCenyId, mode=IN, jdbcType=NUMERIC}, " +
"#{walutaId, mode=IN, jdbcType=NUMERIC}, " +
"#{jmId, mode=IN, jdbcType=NUMERIC}, " +
"#{ilosc, mode=IN, jdbcType=DECIMAL}, " +
"#{data, mode=IN, jdbcType=DATE})}")
#Result(property = "kwota", column = "kwota", javaType = BigDecimal.class, jdbcType = JdbcType.DECIMAL)
#Options(statementType = StatementType.CALLABLE)
public DtoCena dajCene(CriteriaCena parametry);
The procedure selects one row - I am interested in one column. Now, I've mapped a procedure before, only I had multiple rows and selected more then one column from them. Everything worked perfectly fine. When I mapped new procedure, in a similar way I got an error:
### The error occurred while setting parameters
### SQL: {call sp_cen_obliczcene(?, ?, ?, ?, ?, ?)}
### Cause: java.lang.NullPointerException
I started the SQL Profiler and saw that the procedure is called properly with the given parameters. I've noticed that the procedure I'm mapping is executing other procedures. They're performing some updates. When I changed my annotation to #Update I got an other error: that Integer cannot be cast to DtoCena type. I changed the return value of the method to Integer and I got no errors but as you can guess it did not return what I was looking for.
The question is, can I map a stored procedure which updates tables AND returns a ResultSet? I can do this using JDBC, but is this possible with MyBatis? Am I doing something wrong when using the #Select annotation?

Looks like the #Update returns the affected row count ...
Anyway, I don't think the issue is related to calling stored procedure, this is merely a mapping issue that would occur with simple select.
You must use #Result annotation inside #Results annotation, otherwise it is ignored.
Here is a simplified, yet functional, code:
#Select("select 'hello' as h, 1 as n from dual")
#Results({
#Result(column="n")
})
Integer test();
Just add a property attribute and change return type to retrieve result into an object.

Related

Unable to pass empty string into non-null database field

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.

How to add parameters and execute a generic IDbCommand

Here is my problem in detail.
I have created a data access layer class that allows me to create most of objects I needed to communicate with databases (Odbc, OleDb and SqlClient). I also have created a business object handling layer class with intensive use of Reflection to handle lots of tasks with my business object(s). Among other things this class generates every single property/object I needed for my DAL handling (SQL stream, list of values, properties, set retrieve values etc.). Take a look the code below for further explanation:
Public Shared Function InvokeParam(Of T)(_classObject As T, _commandType As AdapterCommandType, _arguments As Object()) As Boolean
Dim s As String = DAL.SCRFL.GetParamStatement(_classObject, _commandType, _arguments)
'Debug.Print(s)
Dim hT As Hashtable = DAL.SCRFL.GetProperties(_classObject)
Using cnn As IDbConnection = DataFactory.CreateConnection()
Dim cmd As IDbCommand = DataFactory.CreateCommand(s, cnn)
'cmd.CommandType = CommandType.Text
cmd.CommandText = s
For Each k In hT
Dim param As IDbDataParameter = cmd.CreateParameter()
'param.DbType = DataFactory.ConvertToDbType(k.value.GetType)
param.Value = k.value
param.ParameterName = k.key
'param.Direction = ParameterDirection.Input
'Debug.Print("value:={0}, name:={1}", TypeName(k.value), TypeName(k.key))
Debug.Print("typeMatch:={0}, value:={1}, name:={2}", TypeName(param.Value) = TypeName(k.value), param.Value, param.ParameterName)
cmd.Parameters.Add(param)
Next
If (cmd.ExecuteNonQuery > 0) Then
Return True
End If
End Using
Return False
End Function
So, DAL.SCRFL.GetParamStatement returns string formatted as INSERT INTO t1 (f1, f2, f3...) values (?, ?, ?...) for insert and appropriate strings for update, delete, select statements. All are done with reflection. There is no syntax error here. I can manually execute returned values through direct provider type commands.
The DAL.SCRFL.GetProperties method returns a hashtable formatted as key=property (field), value=field value.
Now, I need to create parameters for each property and add that to my command parameters then execute it. This attempt you will see in my code (I’m creating parameters for each propert/value pair by looping the hash table). However at the end I'm getting an exception with Data type mismatch in criteria expression. description. I've tried adding type property to parameter object, size etc., all was unsuccessful (I commented them). I tried changing param.Value = k.value to param.Value = If(IsDBNull(k.value), DBNull.Value, k.value) thinking that this might be the problem, though k.value is from my business class and I intentionally prevent from null values. Nothing worked! here is the test; business class returned value from DAL.SCRFL.GetParamStatement call: The test is done for OleDb/Access database and, as you can see, I enclosed the Memo field in single quotes. My reflection methods read class properties' attributes (which I set to be table field names) and DAL.SCRFL.GetParamStatement builds basic sql statements for insert, update, delete and select use. AdapterCommandType is a built in enum type for it).
INSERT INTO Clinics
(ClinicId, ClinicName, Phone, Fax, FederalId, DateContracted, Address, City, State, Zip, Inactive, [Memo], DateEntered, EnteredBy, DateModified, ModifiedBy)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Note that I have another method similar to this that executes an sql statement (InvokeSql) where I thoroughly check value types for each property to construct property=value pairs in my sql statement. Using a fully qualified sql statement in this, InvokeSql, method works w/out a single warning (Rouphly: cnn As IDbConnection = CreateConnection(), cmd = CreateCommand(_cmdText, cnn), cmd.ExecuteNonQuery() where _cmdText is the sql statement. No parameters as you can see!). I'm mentioning this to point out that the problem arises whenever I use parameters with generic IDbCommands. Even though inside my DataFactory the IDbCommand set to be provider specific command type (my DataFactory.CreateCommand(s, cnn) returns generic IDbCommand).
Prior to my DAL development, I was doing all above steps manually though all objects (commands, connections etc.) were explicitly declared to be provider specific types. Technically speaking I’m exercising exact same scenarios as I used to with generic type of objects (not provider specific). But I can’t make it work, there is somewhere, probably, I’m missing something.
I had posted this question on codeproject and the answer is there.
http://www.codeproject.com/Questions/446516/How-to-add-parameters-and-execute-a-generic-IDbCom

Execute multiple stored procedures with single trip to database

I have a lot of legacy data access code mainly SqlCommand with Stored Procedure calls that we used to execute alot of Insert statment into an database.
As long as the SQL server has been on the same machine as the application there have been acceptable performace but now are we trying to move some of the data to SQL Azure.
The problem is that our code calls a SP for every record to insert which results in quite a few trips to the database and when not located on the same server it takes some time.
var conn = new SqlConnection("connString")
var cmd = new SqlCommand(conn, "spMyStoreProc");
cmd.Params.Add("#a", SqlDbType.VarChar, 10);
cmd.Params.Add("#b", SqlDbType.Int);
using(conn)
{
conn.Open();
foreach(var rec in recordsToInsert)
{
cmd.Parameters["#a"].Value = rec.A;
cmd.Parameters["#b"].Value = rec.B;
cmd.ExecuteNonQuery();
}
conn.Close();
}
I have tried the code above with and without Transactions.
I have also tried to use a "batch" SQL statement to execute several SPs in every trip to the server.
Like this:
var cmd = new SqlCommand(conn);
cmd.CommandText = "EXEC spMyStoreProc #a='a' #b=2; EXEC spMyStoreProc #a='b' #b=4;"
It greatly increases the performance of the operation but since I have quite a few SPs where every SP has about 20-50 params it gets quite tedious to write this code for all the insert commands in this data access component.
Is this the best way to achive this, or can I somehow tell ADO.NET I want to execute my calls as a batch (havent fount anything suggesting its possible but feel that I atleast should ask) to avoid network latency etc betweeen every single SP call?
If not does anybody know any good way to achive this without having to write it "by hand" and since its a legacy application I can not change the data layer completely.
Is there any applications that can take SqlCommands with parameters and generate the TQL they would execute?
Thanks in advance
You should probably have one stored procedure, that calls all the other stored procedures - it will probably be the least amount of work. So, from the code you only call the stored procedures once... so given that they are the same parameters you are passing every time (because your code seems to imply that) you would basically do something like this:
CREATE PROCEDURE sp_RunBatch(#param1, #param2, etc [all the parameters you need])
AS
exec spMyStoreProc #a='a'
exec spMyStoreProc2 #b='b'
The advantages of this are many, some of which being that its all centralized, and you can even wrap all of them within a transaction, so as not to do dirty inserts (given that they all depend on each other).
Also, if you don't feel like passing 20/30 parameters to each SP, you may want to make a user-table-defined data-type for each set of parameters, that you can pass. So then each SP gets 1 or 2 parameters, and the code becomes much simpler and readable.
EDIT:
This is a good reference for the user-defined table types: http://msdn.microsoft.com/en-us/library/bb675163.aspx
And this is how to pass the table valued types to SQL server: http://msdn.microsoft.com/en-us/library/bb675163.aspx
An alternative to M.R.'s approach would be to send all your parameters as an XML document, then parse the XML document to extract your parameters. This may simplify the interface a bit.
However I think you were on something when you discussed the possibility of chaining all the commands in a single string. But instead of manually building them, consider building an extension method to the SqlCommand object that returns a single string for execution, leveraging the sp_executesql syntax, and execute the entire string in a single pass.
So you would have a loop that looks like this, and you would call a new ToInlineSql extension method:
string sqlCommand = "";
foreach(var rec in recordsToInsert)
{ cmd.Parameters["#a"].Value = rec.A;
cmd.Parameters["#b"].Value = rec.B;
sqlCommand += cmd.ToInlineSql();
}
// execute sqlCommand
The ToInlineSql extension method could look like this (peuso-code, you will have to add certain things such as checking for the data type and so on) [and here is the link to sp_executesql:
public static class SqlCmdExt
{
public static string ToInlineSql(this SqlCommand cmd)
{
string sql = "sp_executesql " + cmd.CommandText ;
foreach (SqlParameter p in cmd.Parameters)
{
sql += ", #" + p.Name + " " + p.DataType.ToString() ;
sql += ", " + p.Name + " = " + p.Value;
}
sql += ";";
return sql;
}
}

INSERT variable values into a table

I have several variables in an SSIS package that I would like inserting into a table.
example:-
#financialMonth, #Status, #Comments
The Variables have been populated along the way with values based on lookups, filename, dates, etc, and I want to store them in a results table.
Is using the execute SQL task the way to do this ?
Do I need to call a sproc and pass those variales as parameters ?
I've tried putting the following T-SQL into the SQLStatement property
INSERT INTO FilesProcessed
(ProcessedOn, ProviderCode, FinancialMonth,
FileName, Status, Comments)
SELECT GETDATE(), 'ABC' , 201006,
'ABC_201005_Testology.csv',
'Imported','Success'
I tried hardcoding the values above to get it to work
These are the columns on the table I'm inserting into
Column_name Type Computed Length
fileID int no 4
ProcessedOn datetime no 8
ProviderCode nchar no 6
FinancialMonth int no 4
FileName nvarchar no 510
Status nvarchar no 40
Comments nvarchar no 510
This is the Expression code that feeds the SQLStatementSource property
"INSERT INTO FilesProcessed (ProcessedOn, ProviderCode, FinancialMonth,
FileName, Status, Comments) SELECT GETDATE() AS ProcessedOn, '"
+ #[User::providerCode] + "' , "
+ (DT_STR,6,1252)#[User::financialMonth] + ", '"
+ #[User::fileName] + "', 'Imported' AS Status,'Successfully' AS Comments "
Unfortunately I'm missing something, and can't quite get it to work.
The Error message I'm getting is ...
Error: 0xC002F210 at Log entry in
FilesProcessed, Execute SQL Task:
Executing the query "INSERT INTO
FilesProcessed (ProcessedOn,
ProviderCode, FinancialMonth,
FileName, Status, Comments) SELECT
GETDATE(), 'ABC' , 201006,
'DAG_201005_Testology.csv',
'Imported','Successfully'" failed with
the following error: "An error
occurred while extracting the result
into a variable of type (DBTYPE_I2)".
Possible failure reasons: Problems
with the query, "ResultSet" property
not set correctly, parameters not set
correctly, or connection not
established correctly.
Please
a). Advise whether the Execute SQL Task is the way to do what I want to do.
b). Give me any pointers or pitfalls to look out for and check.
Thanks in advance.
OK, here is what I did.
I created an Execute SQL task and configured, thus :-
General Tab
ConnectionType = OLE DB
SQLSourceType = Direct Input
SQLStatement = (left blank)
BypassPrepare = True
ResultSet = None
Parameter Mapping
(none - leave blank)
Result Set
(none - leave blank)
Expressions
SQLStatementSource = "INSERT INTO FilesProcessed (ProcessedOn, ProviderCode, FinancialMonth, FileName, Status, Comments) SELECT GETDATE(), '" + #[User::providerCode] + "' , " + (DT_STR,6,1252)#[User::financialMonth] + ", '" + #[User::fileName] + "', 'Import - Success', '" + #[User::fileComments] + "'"
Then as long as I set up the variables and populate them in the variables window (the Expression editor will not let you save an expression that references a variable that does not exist. Keep notepad handy to store the contents while you go back and edit the variables window, and add new variables in ;)
Build the expression slowly, using the Parse expression button regularly to check.
make sure that the data types of the VALUES match the destination column data types.
see: http://social.msdn.microsoft.com/forums/en-US/sqlintegrationservices/thread/e8f82288-b980-40a7-83a6-914e217f247d/
A couple of speculative suggestions
The Error message says An error occurred while extracting the result into a variable of type (DBTYPE_I2). But this is a straight insert statement. There shouldn't be a result except for rows affected. Do you have any parameter mappings erroneously set to Output?
What if you try and run the SQL Query from the error message directly in management studio? Does that give you an error?
In the above table definition FinancialMonth as int datatype as
FinancialMonth int no 4
while inseting casting as :
(DT_STR,6,1252)#[User::financialMonth]
I think it's purely a datatype mismatch with the target table definition.

Delphi: "Parameter object is improperly defined. Inconsistent or incomplete information was provided."

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.

Resources