Failing to insert binary stream into a BLOB in hsqldb? - hsqldb

I have the following hsqldb schema (as reported by SQLWorkbench):
DROP TABLE TEST CASCADE;
CREATE TABLE TEST
(
NAME VARCHAR(256),
METADATA VARCHAR(2048),
DATA BLOB
);
GRANT TRIGGER, INSERT, REFERENCES, DELETE, SELECT, UPDATE ON TEST TO DBA;
Next, I am trying to insert a file into the DATA field using the following prepared statement:
MERGE INTO test USING (VALUES ?, ?, ?) I (name, metadata, data) ON (test.name=I.name) WHEN MATCHED THEN UPDATE SET test.data = I.data, test.metadata = I.metadata WHEN NOT MATCHED THEN INSERT (name, metadata, data) VALUES (I.name, I.metadata, I.data)
Here is the code:
String name = ...;
String metadata = ...;
InputStream data = ...;
JDBCDataSource ds = new JDBCDataSource();
ds.setDatabase("jdbc:hsqldb:file:c:/tmp/file.db");
ds.setUser("sa");
ds.setPassword("");
PreparedStatement set = ds.getConnection().prepareStatement(m_setSql);
set.setString(1, name);
set.setString(2, metadata);
set.setBinaryStream(3, data);
set.executeUpdate();
The setBinaryStream fails, because the parameter type is deemed to be VARCHAR, rather than BLOB. Indeed, the function org.hsqldb.jdbc.JDBCPreparedStatement.setBinStream has the following statement:
if (parameterTypes[parameterIndex - 1].typeCode == Types.SQL_BLOB) {
setBlobParameter(parameterIndex, x, length);
return;
}
For the parameterIndex 3 it should enter the if-statement and invoke the setBlobParameter. But, for some reason, typeCode returns 12, which corresponds to VARCHAR, the if-statement is skipped and in the end an org.hsqldb.HsqlException is raised with the message of incompatible data type in conversion.
What am I doing wrong?

The types of the parameter values in the MERGE statement are unknown and default to VARCHAR. You need to cast the BLOB parameter to BLOB.
MERGE INTO test USING (VALUES ?, ?, CAST(? AS BLOB)) I (name, metadata, data)
ON (test.name=I.name)
WHEN MATCHED THEN UPDATE SET test.data = I.data, test.metadata = I.metadata
WHEN NOT MATCHED THEN INSERT (name, metadata, data) VALUES (I.name, I.metadata, I.data)

Related

Update or add value with id

I am just learning SQLDelight and was hoping to find how to add an object with an ID if it doesn't exist already if it does exist then update that current object with the given id.
currently, I am deleting the current object with id, then adding an object and was hoping to reduce this into one simple call.
My current code:
CREATE TABLE Color (
id TEXT NOT NULL,
name TEXT NOT NULL,
hex TEXT NOT NULL
);
getColorWithId:
SELECT * FROM Color
WHERE id = ?;
saveColor:
INSERT OR REPLACE INTO Color (id, name, hex)
VALUES (?, ?, ?);
deleteColorWithId:
DELETE FROM Color
WHERE id = ?;
I was hoping to change it to replace saveColor and deleteColorWithId with something like:
updateColorWithId:
INSERT OR REPLACE INTO Color (id, name, hex)
WHERE id = ?
VALUES (?, ?, ?);
but it doesn't work with this error <insert stmt values real> expected, got 'WHERE'
can anyone help? I can't find anything in the docs.
Your statement saveColor serves as UPSERT command and it works exactly as you wish. You don't need to create another statement.
INSERT OR REPLACE INTO Color (id, name, hex)
VALUES (?, ?, ?);
You must specify PRIMARY KEY on id column and you can use saveColor as updateColorWithId.
You are already there, try something like this:
Check if the record exists. If it does, then update otherwise add a new row. Using named arguments as per documentation would be ideal. https://cashapp.github.io/sqldelight/native_sqlite/query_arguments/
updateOrInsert:
IF EXISTS (SELECT 1 FROM Color WHERE id = :id)
BEGIN
UPDATE Color
SET id = :id,
name = :name,
hex = :hex
WHERE id = :id
END
ELSE
BEGIN
INSERT INTO Color(id, name, hex)
VALUES (:id, :name, :hex);
END
Usage
dbQuery.updateOrInsert(id = BGColor.id, name = bgColor.name, hex = bgColor.hex)
Take a look at this, might be useful REPLACE INTO vs Update

How do I use parameters for the source in a MERGE statement in Informix?

I am trying to execute a merge statement against an Informix database as follows:
MERGE INTO aa_rec AS dest
USING (SELECT '123456' AS id, '111-222-3333' as phone, '' as phone_ext, 'CELL' as aa FROM sysmaster:'informix'.sysdual) AS src
ON dest.id = src.id AND dest.aa = src.aa
WHEN NOT MATCHED THEN
INSERT (dest.id, dest.aa, dest.beg_date, dest.phone, dest.phone_ext, dest.ofc_add_by)
VALUES (src.id, src.aa, TODAY, src.phone, src.phone_ext, 'TEST')
WHEN MATCHED THEN UPDATE SET
dest.phone = src.phone,
dest.phone_ext = src.phone_ext,
dest.beg_date = '10/29/2019',
dest.ofc_add_by = 'TEST'
This statement works as is, with hard-coded values, but I would like to pass parameters for the values in the source table:
USING (SELECT ? AS id, ? as phone, ? as phone_ext, 'CELL' as aa FROM sysmaster:'informix'.sysdual) AS src
When I execute the statement with parameters and valid values, I receive this error:
E42000: (-201) A syntax error has occurred.
Are parameters supported in the source part of the MERGE statement? If they are, where is the error in my syntax?
For context, I'm calling this from ASP.NET using the OleDb provider for Informix.
You have:
SELECT ? AS id, ? as phone, ? as phone_ext, 'CELL' as aa FROM sysmaster:'informix'.sysdual
You can't use placeholders (? symbols) for 'structural' elements of a SELECT statement. You can't provide column names in the placeholders. And passing numbers etc as values via placeholders in the select-list doesn't work either.
I'd probably create a temp table of the appropriate shape, and insert a row into that, and then use the temp table in the select statement:
SELECT '123456' AS id, '111-222-3333' AS phone, '' AS phone_ext, 'CELL' AS aa
FROM sysmaster:'informix'.sysdual
INTO TEMP phone_data;
MERGE INTO aa_rec AS dest
USING (SELECT * FROM phone_data) AS src
ON dest.id = src.id AND dest.aa = src.aa
WHEN NOT MATCHED THEN
INSERT (dest.id, dest.aa, dest.beg_date, dest.phone, dest.phone_ext, dest.ofc_add_by)
VALUES (src.id, src.aa, TODAY, src.phone, src.phone_ext, 'TEST')
WHEN MATCHED THEN UPDATE SET
dest.phone = src.phone,
dest.phone_ext = src.phone_ext,
dest.beg_date = '10/29/2019',
dest.ofc_add_by = 'TEST'
;
DROP TABLE phone_data;
It might be better/safer to create the temp table explicitly rather than to use the INTO TEMP clause. The types are not necessarily what you'd expect (CHAR(6), CHAR(12), VARCHAR(1), CHAR(4)) — though that may not matter.
Clearly, once the temp table exists, you can insert whatever data is appropriate into the temp table using any mechanism that's available:
INSERT INTO phone_data(id, phone, phone_ext, aa) VALUES(?, ?, ?, ?)
Remember that temp tables are private to a session — you can have lots of people all using the same temporary table name at the same time without interfering with each other.

How to manually insert a BLOB into a SQLite database

I want to know how to manually insert a BLOB into my SQLite database. By manually I mean, without using a driver feature that will complete the command like setBytes:
Connection con = DriverManager.getConnection("jdbc:sqlite:database.db");
PreparedStatement stmt = con.prepareStatement("INSERT OR REPLACE INTO test (id, aBlobColumn) VALUES (0, ?)";
stmt.setBytes(1, new byte[] {0x37, 0xe7, 0x9f});
stmt.executeUpdate();
Is it possible to use a command like that:
INSERT OR REPLACE INTO test (id, aBlobColumn) VALUES (0, 37e79f);
or like that:
INSERT OR REPLACE INTO test (id, aBlobColumn) VALUES (0, BLOB(37, e7, 9f));
I don't mind if the command includes base64 data or raw data, I don't want to specifically use hexadecimal.
You can use the following :-
INSERT OR REPLACE INTO test (id, aBlobColumn) VALUES (0, x'37e79f');
However, the value has to be a hex string for it to be a BLOB.

ORA-01461: can bind a LONG value only for insert into a LONG column - when inserting into CLOB

I am inserting a large string into a CLOB column. The string is (in this instance) 3190 characters long - but can be much larger.
The string consists of xml data - sometimes the data will commit, sometimes i get the error. The error occurs roughly 50% of the time.
Even string which contain over 5000 characters will sometimes commit with no problem.
Unsure where to go next as i am under the impression that CLOB is the best data type for this data.
I have tried LONG LONG RAW
Someone suggested using XMLTYPE however that does not exist in my version of Oracle (11g - 11.2.0.2.0)
My insert statement:
INSERT INTO MYTABLE(InterfaceId, SourceSystem, Description, Type, Status, StatusNotes, MessageData, CreatedDate, ChangedDate, Id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
MessageData is the CLOB column where the error is occuring, i have tried commiting without this data populated and it works.
Error
ORA-01461: can bind a LONG value only for insert into a LONG column
ALTER TABLE MYTABLE
ADD COLUMN XML_COL XMLTYPE;
AND THEN
SQL> INSERT INTO MYTABLE(..., XML_COL) VALUES (..., XMLTYPE('<root>example</root>'));
The key is to use XMLTYPE column and then use XMLTYPE() function to convert your string to XMLTYPE.

Inserting Long Binary data into Access db using pyodbc

I can insert text and integer data to MS Access db (.mdb) by using pyodbc package. But now i want to insert Large Binary objects. I have a table that consists ID(COUNTER type), Name(VARCHAR type), File (LONGBINARY type), Author(VARCHAR type) columns. I use that code to insert some text and int data:
cursor.execute("""INSERT INTO table(ID, Name) VALUES(1,'book')""")
After that i used that code but always getting error.
with open('c:/tree.jpg', 'rb') as file:
binData = file.read()
SQL = """INSERT INTO table VALUES(2,'threePicture', %s, 'Mike')""" %(binData)
cursor.execute(SQL)
The error is: ProgrammingError: ('42000', "[42000])
I found the solution using ? ? ? characters...
cursor.execute("insert into table values(?, ?, ?, ?)", 2, 'treePicture', pyodbc.Binary(binData), 'Mike')
Use ? chars for values in expression.