SQLITE_RANGE: bind or column out of range for INSERT statement - sql

I want to insert rows into a SQLite3 table using the knex.raw method. Unfortunately I get a 'SQLITE_RANGE' error, which makes my test fail.
I have verified the bindings passed to the raw query in the following fashion:
They respect the order of the INSERT statement
They respect the specified column types
They respect the number of bindings requested in the raw query
Beyond that I have looked online, but couldn't find a solution to my issue. Below are the details of the operation attempted:
Engine: sqlite3 ^3.1.13
SQL Client: knex ^0.14.4
Environment: electron ^1.7.11
Error:
SQLITE_RANGE: bind or column index out of range errno: 25, code: 'SQLITE_RANGE'
Table definition:
-- --------------------------------------------------------
--
-- Table structure for table `ds13odba`
--
CREATE TABLE IF NOT EXISTS `ds13odba` (
`SURGERY_CODE` VARCHAR(6) ,
`TYPE` VARCHAR(1) ,
`FP59STALIB` VARCHAR(6) ,
`ID` INT UNSIGNED NOT NULL ,
`createdAt` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
`updatedAt` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL
);
-- --------------------------------------------------------
*Take note that the column types defined here are affinity types, i.e. MySQL types. These are valid in SQLite3 and are casted and optimized by the engine to their equivalent in SQLite3.
Query:
INSERT INTO `ds13odba` (FP59STALIB, ID, SURGERY_CODE, TYPE) VALUES (?,?,?,?);
INSERT INTO `ds13odba` (FP59STALIB, ID, SURGERY_CODE, TYPE) VALUES (?,?,?,?);
INSERT INTO `ds13odba` (FP59STALIB, ID, SURGERY_CODE, TYPE) VALUES (?,?,?,?);
Bindings:
[
'047202', 1, '000001', 'D',
'047203', 2, '000002', 'D',
'047204', 3, '000003', 'D'
]
Calling code:
await knex.raw(...convertToInsertSQL(records));
Which resolves to:
await knex.raw(insertStatements.join('\n'), bindings);
Could you help me with this issue?
Cheers 🦋

The issue stems from SQLite3's lack of support of multi-statements per exec() call, as documented here.
After some testing on my end, I discovered that the SQLite3 engine will assign automatically all the bindings to the first statement of the prepared SQL. Any following statements will be ignored.
This still applies for transactions, as the bindings will be applied to the 'BEGIN TRANSACTION;' statement rather than to the following statements.
The solution is to use a compound INSERT statement with bindings.
Hence this:
INSERT INTO `ds13odba` (FP59STALIB, ID, SURGERY_CODE, TYPE) VALUES (?,?,?,?);
INSERT INTO `ds13odba` (FP59STALIB, ID, SURGERY_CODE, TYPE) VALUES (?,?,?,?);
INSERT INTO `ds13odba` (FP59STALIB, ID, SURGERY_CODE, TYPE) VALUES (?,?,?,?);
becomes this:
INSERT INTO `ds13odba` (FP59STALIB, ID, SURGERY_CODE, TYPE)
VALUES (?,?,?,?), (?,?,?,?), (?,?,?,?);
*Bear in mind that compound INSERT statements are only available as of version 3.7.11 of the SQLite3 engine.

I don't see anything obviously wrong with the info you've posted, but you haven't posted the actual .raw() statements, which would help with debugging.
So attempting to assist, I would suggest that you add an .on('query-error'... clause like that below, which will log the SQL that is failing. Many times this will make the problem obvious.
knex.raw(...your-stuff...)
.on('query-error', function(ex, obj) {
console.log("KNEX-query-error ex:", ex, "obj:", obj);
})
Good luck!

Related

How can i use default value on postgres when query include null value

I use Postgres and I've integration app which write data to database. My column should not be null but my app send null value. I tried to set default value but query override this rule with null value. How can i handle this change without code.
My Column configuration looks like this.
If you won't or can't change the query in code, you have to use trigger
If you can change code structure and query:
If the column has a default value, then no need to send NULL value to query
-- Before change
insert into your_table (id, name, default_col) values
(1, 'name', null);
-- After change (remove null data)
insert into your_table (id, name) values
(1, 'name');
Or send default value in insert query
-- Before change
insert into your_table (id, name, default_col) values
(1, 'name', null);
-- After change (Use default keyboard)
insert into your_table (id, name, default_col) values
(1, 'name', default);

syntax error when trying to input date and time

I just started with SQL and I'm having a problem when trying to insert an date and time.
The table structure:
CREATE TABLE Voo_Pac
(
codReserva INT NOT NULL PRIMARY KEY,
DataCont DATE,
HoraCont TIME
);
Code I'm trying to use to insert date and time:
INSERT INTO Voo_Pac (codReserva, DataCont, HoraCont)
VALUES (1), (15-08-2019), (12:13:52);
When I try to execute the code, it gives me the following message:
Error 1: could not prepare statement (1 near ":13": syntax error)
I assume you are using MySQL/MariaDB/SQL Server because of the TIME datatype?
Your insert should be
INSERT INTO Voo_Pac (codReserva, DataCont, HoraCont)
VALUES (1, '2019-08-15', '12:13:52');
see demo
You at least need quotes. And depending on your DB maybe a CAST to the apropiated type
INSERT INTO Voo_Pac (codReserva, DataCont, HoraCont)
VALUES 1, '15-08-2019', '12:13:52';

Informix Blob data

I need to know if there is any possibility of passing null in blob data using insert statement in Informix.
I know there is an option of LOAD FROM <FILE> INSERT INTO <TABLE>
Which works well, but i am looking for some insert statment, because i am implementing liquibase and i am not finding any way to insert null or leave it in insert values so that it can accept null. This field is set to TRUE for null values.
Error I am getting is following
[Error Code: -617, SQL State: IX000] A blob data type must be supplied within this context.
Tried this
INSERT INTO informix.tti_key_store (id, code, description, value_asc, cts, active)
VALUES
(4, 'EVDSDEALER', 'EVDS Dealer Passphrase', 'nodYzUNAs+htD1Mng3hYYg==', DATETIME (2016-02-17 12:51:34.000) YEAR TO FRACTION(5), 'T')
Tried this
INSERT INTO informix.tti_key_store (id, code, description, value_asc, value_bin, cts, active)
VALUES
(4, 'EVDSDEALER', 'EVDS Dealer Passphrase', 'nodYzUNAs+htD1Mng3hYYg==', NULL, DATETIME (2016-02-17 12:51:34.000) YEAR TO FRACTION(5), 'T')
value_bin is a blob field set to NULL to true.

How to to get the value of an auto increment column in postgres from a .sql script file?

In postgres I have two tables like so
CREATE TABLE foo (
pkey SERIAL PRIMARY KEY,
name TEXT
);
CREATE TABLE bar (
pkey SERIAL PRIMARY KEY,
foo_fk INTEGER REFERENCES foo(pkey) NOT NULL,
other TEXT
);
What I want to do is to write a .sql script file that does the following
INSERT INTO foo(name) VALUES ('A') RETURNING pkey AS abc;
INSERT INTO bar(foo_fk,other) VALUES
(abc, 'other1'),
(abc, 'other2'),
(abc, 'other3');
which produces the error below in pgAdmin
Query result with 1 row discarded.
ERROR: column "abc" does not exist
LINE 3: (abc, 'other1'),
********** Error **********
ERROR: column "abc" does not exist
SQL state: 42703
Character: 122
Outside of a stored procedure how do a define a variable that I can use between statements? Is there some other syntax for being able to insert into bar with the pkey returned from the insert to foo.
You can combine the queries into one. Something like:
with foo_ins as (INSERT INTO foo(name)
VALUES ('A')
RETURNING pkey AS foo_id)
INSERT INTO bar(foo_fk,other)
SELECT foo_id, 'other1' FROM foo_ins
UNION ALL
SELECT foo_id, 'other2' FROM foo_ins
UNION ALL
SELECT foo_id, 'other3' FROM foo_ins;
Other option - use an anonymous PL/pgSQL block like:
DO $$
DECLARE foo_id INTEGER;
BEGIN
INSERT INTO foo(name)
VALUES ('A')
RETURNING pkey INTO foo_id;
INSERT INTO bar(foo_fk,other)
VALUES (foo_id, 'other1'),
(foo_id, 'other2'),
(foo_id, 'other3');
END$$;
You can use lastval() to ...
Return the value most recently returned by nextval in the current session.
This way you do not need to know the name of the seqence used.
INSERT INTO foo(name) VALUES ('A');
INSERT INTO bar(foo_fk,other) VALUES
(lastval(), 'other1')
, (lastval(), 'other2')
, (lastval(), 'other3')
;
This is safe because you control what you called last in your own session.
If you use a writable CTE as proposed by #Ihor, you can still use a short VALUES expression in the 2nd INSERT. Combine it with a CROSS JOIN (or append the CTE name after a comma (, ins) - same thing):
WITH ins AS (
INSERT INTO foo(name)
VALUES ('A')
RETURNING pkey
)
INSERT INTO bar(foo_fk, other)
SELECT ins.pkey, o.other
FROM (
VALUES
('other1'::text)
, ('other2')
, ('other3')
) o(other)
CROSS JOIN ins;
Another option is to use currval
INSERT INTO foo
(name)
VALUES
('A') ;
INSERT INTO bar
(foo_fk,other)
VALUES
(currval('foo_pkey_seq'), 'other1'),
(currval('foo_pkey_seq'), 'other2'),
(currval('foo_pkey_seq'), 'other3');
The automatically created sequence for serial columns is always named <table>_<column>_seq
Edit:
A more "robust" alternative is to use pg_get_serial_sequence as Igor pointed out.
INSERT INTO bar
(foo_fk,other)
VALUES
(currval(pg_get_serial_sequence('public.foo', 'pkey')), 'other1'),
(currval(pg_get_serial_sequence('public.foo', 'pkey')), 'other2'),
(currval(pg_get_serial_sequence('public.foo', 'pkey')), 'other3');

How to do an insert with multiple rows in Informix SQL?

I want to insert multiple rows with a single insert statement.
The following code inserts one row, and works fine:
create temp table mytmptable
(external_id char(10),
int_id integer,
cost_amount decimal(10,2)
) with no log;
insert into mytmptable values
('7662', 232, 297.26);
select * from mytmptable;
I've tried changing the insert to this, but it gives a syntax error:
insert into mytmptable values
('7662', 232, 297.26),
('7662', 232, 297.26);
Is there a way to get it working, or do I need to run many inserts instead?
You could always do something like this:
insert into mytmptable
select *
from (
select '7662', 232, 297.26 from table(set{1})
union all
select '7662', 232, 297.26 from table(set{1})
)
Pretty sure that's standard SQL and would work on Informix (the derived table is necessary for Informix to accept UNION ALL in INSERT .. SELECT statements).
As you found, you can't use multiple lists of values in a single INSERT statement with Informix.
The simplest solution is to use multiple INSERT statements each with a single list of values.
If you're using an API such as ESQL/C and you are concerned about performance, then you can create an INSERT cursor and use that repeatedly. This saves up the inserts until a buffer is full, or you flush or close the cursor:
$ PREPARE p FROM "INSERT INTO mytmptable VALUES(?, ?, ?)";
$ DECLARE c CURSOR FOR p;
$ OPEN c;
while (...there's more data to process...)
{
$PUT c USING :v1, :v2, :v3;
}
$ CLOSE c;
The variables v1, v2, v3 are host variables to hold the string and numbers to be inserted.
(You can optionally use $ FLUSH c; in the loop if you wish.) Because this buffers the values, it is pretty efficient. Of course, you could also simply use $ EXECUTE p USING :v1, :v2, :v3; in the loop; that foregoes the per-row preparation of the statement, too.
If you don't mind writing verbose SQL, you can use the UNION technique suggested by Matt Hamilton, but you will need a FROM clause in each SELECT with Informix. You might specify:
FROM "informix".systables WHERE tabid = 1, or
FROM sysmaster:"informix".sysdual, or
use some other technique to ensure that the SELECT has a FROM clause but only generates one row of data.
In my databases, I have either a table dual with a single row in it, or a synonym dual that is a synonym for sysmaster:"informix".sysdual. You can get away without the "informix". part of those statements if the database is 'normal'; the owner name is crucial if your database is an Informix MODE ANSI database.
In some versions of Infomix you can build a virtual table using the TABLE keyword followed by a value of one of the COLLECTION data types, such as a LIST collection. In your case, use a LIST of values of Unnamed Row type using the ROW(...) constructor syntax.
Creating a TABLE from COLLECTION value
http://www.ibm.com/support/knowledgecenter/SSGU8G_11.50.0/com.ibm.sqls.doc/ids_sqs_1375.htm
ROW(...) construction syntax, for literals of Unnamed Row data type
http://www.ibm.com/support/knowledgecenter/SSGU8G_11.50.0/com.ibm.sqlr.doc/ids_sqr_136.htm
Example:
select *
from TABLE(LIST{
ROW('7662', 232, 297.26),
ROW('7662', 232, 297.26)
}) T(external_id, int_id, cost_amount)
into temp mytmptable with no log
In the above, the data types are implied by the value, but when needed you can explicitly cast each value to the desired data type in the row constructor, like so:
ROW('7662'::char(10), 232::integer, 297.26::decimal(10,2))
You can also insert multiple rows by storing the values in an external file and executing the following statement in dbaccess:
LOAD FROM "externalfile" INSERT INTO mytmptable;
However, the values would have to be DELIMITED by a pipe "|" symbol, or whatever you set the DBDELIMITER environment variable to be.
If you're using the pipe delimiter, the data in your external file would look like:
7662|232|297.26|
7663|233|297.27|
...
NOTE that the data in the external file must be properly formatted or able to be converted to successfully be inserted into each mytmptable.column datatype.
Here is a simple solution fro bulk insert with SELECT part solving the rest
INSERT INTO cccmte_pp
( cmte, pref, nro, eje, id_tri, id_cuo, fecha, vto1, vto2, id_tit, id_suj, id_bie, id_gru )
SELECT * FROM TABLE (MULTISET {
row('RC', 4, 10, 2020, 1, 5, MDY(05,20,2020), MDY(05,20,2020),MDY(05,27,2020),101, 1, 96, 1 ),
row('RC', 4, 11, 2020, 1, 5, MDY(05,20,2020), MDY(05,20,2020),MDY(05,27,2020),101, 1, 96, 1 ) })
AS t( cmte, pref, nro, eje, id_tri, id_cuo, fecha, vto1, vto2, id_tit, id_suj, id_bie, id_gru )
;