array in prepared statement inside where in clause - c++

prep_stmt = con->prepareStatement("SELECT * FROM table WHERE customers in ( ? ) and alive = ?");
prep_stmt->setString(1,customer_string);
prep_stmt->setInt(2,1);
res = prep_stmt->executeQuery();
Here the customer_string is "12,1,34,67,45,14"
When I pass it as a String it always returns a single row, takes the first value only 12.
The sql statement prepared is:
SELECT * FROM table WHERE customers in ( "12,1,34,67,45,14" ) and alive = 1
but I want sql statement to be prepared as:
SELECT * FROM table WHERE customers in (12,1,34,67,45,14 ) and alive = 1
What is the easiest way to achieve the same in C++?

I am assuming you are using the MySQL C++ Connector. Unfortunately it seems that it is not possible to pass array as parameter of prepared statement using this API:
Connector/C++ does not support the following JDBC standard data types: ARRAY, BLOB, CLOB, DISTINCT, FLOAT, OTHER, REF, STRUCT.
https://dev.mysql.com/doc/connector-cpp/en/connector-cpp-usage-notes.html
You can place the value into the query directly by concatenating strings. Be VERY careful to not introduce SQL injection vulnerability. Alternatively use some other API.

Related

Can I add a string type parameter to a SQL statement without quotes?

I have a C++Builder SQL Statement with a parameter like
UnicodeString SQLStatement = "INSERT INTO TABLENAME (DATETIME) VALUES (:dateTime)"
Can I add the parameter without quotes?
Usually I'd use
TADOQuery *query = new TADOQuery(NULL);
query->Parameters->CreateParameter("dateTime", ftString, pdInput, 255, DateTimeToStr(Now()));
which will eventually produce the SQL String
INSERT INTO TABLENAME (DATETIME) VALUES ('2022-01-14 14:33:00.000')
but because this is a legacy project (of course, it always is) and I have to maintain different database technologies, I need to be able to inject database specific date time conversion methods, so that the endresult would look like
INSERT INTO TABLENAME (DATETIME) VALUES (to_date('2022-01-14 14:33:00.000', 'dd.mm.yyyy hh24:mi:ss')))
If I try injecting this via my 'usual' method (because I don't think I can inject a second parameter into this one) it'd look like:
TADOQuery *query = new TADOQuery(NULL);
query->Parameters->CreateParameter("dateTime", ftInteger, pdInput, 255, "to_date('" + DateTimeToStr(Now()) + "', 'dd.mm.yyyy hh24:mi:ss')");
but of course the result would look like:
INSERT INTO TABLENAME (DATETIME) VALUES ('to_date('2022-01-14 14:33:00.000', 'dd.mm.yyyy hh24:mi:ss')'))
and therefore be invalid
Or is there another way to do this more cleanly and elegantly? Although I'd settle with 'working'.
I can work around this by preparing two SQL Statements and switch the statement when another database technology is but I just wanted to check if there is another way.
Why are you defining the parameter's DataType as ftInteger when your input value is clearly NOT an integer? You should be defining the DataType as ftDateTime instead, and then assigning Now() as-is to the parameter's Value. Let the database engine decide how it wants to format the date/time value in the final SQL per its own rules.
query->Parameters->CreateParameter("dateTime", ftDateTime, pdInput, 0, Now());

Python Cx_Oracle; How Can I Execute a SQL Insert using a list as a parameter

I generate a list of ID numbers. I want to execute an insert statement that grabs all records from one table where the ID value is in my list and insert those records into another table.
Instead of running through multiple execute statements (as I know is possible), I found this cx_Oracle function, that supposedly can execute everything with a single statement and list parameter. (It also avoids the clunky formatting of the SQL statement before passing in the parameters) But I think I need to alter my list before passing it in as a parameter. Just not sure how.
I referenced this web page:
https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-executemany.html
ids = getIDs()
print(ids)
[('12345',),('24567',),('78945',),('65423',)]
sql = """insert into scheme.newtable
select id, data1, data2, data3
from scheme.oldtable
where id in (%s)"""
cursor.prepare(sql)
cursor.executemany(None, ids)
I expected the SQL statement to execute as follows:
Insert into scheme.newtable
select id, data1, data2, data3 from scheme.oldtable where id in ('12345','24567','78945','65423')
Instead I get the following error:
ORA-01036: illegal variable name/number
Edit:
I found this StackOverflow: How can I do a batch insert into an Oracle database using Python?
I updated my code to prepare the statement before hand and updated the list items to tuples and I'm still getting the same error.
You use executemany() for batch DML, e.g. when you want to insert a large number of values into a table as an efficient equivalent of running multiple insert statements. There are cx_Oracle examples discussed in https://blogs.oracle.com/opal/efficient-and-scalable-batch-statement-execution-in-python-cx_oracle
However what you are doing with
insert into scheme.newtable
select id, data1, data2, data3
from scheme.oldtable
where id in (%s)
is a different thing - you are trying to execute one INSERT statement using multiple values in an IN clause. You would use a normal execute() for this.
Since Oracle keeps bind data distinct from SQL, you can't pass in multiple values to a single bind parameter because the data is treated as a single SQL entity, not a list of values. You could use %s string substitution syntax you have, but this is open to SQL Injection attacks.
There are various generic techniques that are common to Oracle language interfaces, see https://oracle.github.io/node-oracledb/doc/api.html#sqlwherein for solutions that you can rewrite to Python syntax.
using temporary table to save ids (batch insert)
cursor.prepare('insert into temp_table values (:1)')
dictList = [{'1': x} for x in ids]
cursor.executemany(None, dictList)
then insert selected value into newtable
sql="insert into scheme.newtable (selectid, data1, data2, data3 from scheme.oldtable inner join temp_table on scheme.oldtable.id = temp_table.id)"
cursor.execut(sql,connection)
the script of create temporary table in oracle
CREATE GLOBAL TEMPORARY TABLE temp_table
(
ID number
);
commit
I hope this useful.

Informix: Modify from CLOB to LVARCHAR

I have a table
CREATE TABLE TEST
(
test_column CLOB
)
I want to change the datatype of test_column to LVARCHAR. How can I achieve this? I tried several things until now:
alter table test modify test_column LVARCHAR(2500)
This works, but the content of test_column gets converted from 'test' to '01000000d9c8b7a61400000017000000ae000000fb391956000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000'.
alter table test add tmp_column LVARCHAR(2500);
update test set tmp_column = DBMS_LOB.SUBSTR(test_column,2500,1);
This does not work and I get the following exception:
[Error Code: -674, SQL State: IX000] Method (substr) not found.
Do you have any further ideas?
Using a 12.10.xC5DE instance to do some tests.
From what i could find in the manuals, there isn't a cast from CLOB to other data types.
CLOB data type
No casts exist for CLOB data. Therefore, the database server cannot convert data of the CLOB type to any other data type, except by using these encryption and decryption functions to return a BLOB. Within SQL, you are limited to the equality ( = ) comparison operation for CLOB data. To perform additional operations, you must use one of the application programming interfaces from within your client application.
The encryption/decryption functions mentioned still return CLOB type objects, so they do not do what you want.
Despite the manual saying that there is no cast for CLOB, there is a registered cast in the SYSCASTS table. Using dbaccess , i tried an explicit cast on some test data and got return values similar to the ones you are seeing. The text in the CLOB column is 'teste 01', terminated with a line break.
CREATE TABLE myclob
(
id SERIAL NOT NULL
, doc CLOB
);
INSERT INTO myclob ( id , doc ) VALUES ( 0, FILETOCLOB('file1.txt', 'client'));
SELECT
id
, doc
, doc::LVARCHAR AS conversion
FROM
myclob;
id 1
doc
teste 01
conversion 01000000d9c8b7a6080000000800000007000000a6cdc0550000000001000000000
0000000000000000000000000000000000000000000000000000000000000000000
0000000000
So, there is a cast from CLOB, but it does not seem to be useful for what you want.
So back to the SQL Packages Extension . You need to register this datablade on the database. The files required are located in the $INFORMIXDIR/extend and you want the excompat.* module. Using the admin API, you can register the module by executing the following:
EXECUTE FUNCTION sysbldprepare('excompat.*', 'create');
If the return value is 0 (zero) then the module should now be registered.
SELECT
id
, DBMS_LOB_SUBSTR(doc, DBMS_LOB_GETLENGTH(doc) - 1, 1) as conversion
FROM
myclob;
id 1
conversion teste 01
Another way would be to register your own cast from CLOB to LVARCHAR, but you would have to code an UDR to implement it.
P.S:
Subtracting 1 from the CLOB length to remove the line break.

Empty blob insert query in ODBC c ++ (oracle)

I need to insert a blob in o oracle database. I am using c++ and ODBC library.
I am stucked at the insert query and update query .It is abstract for me how to make an blob insert query.
I know how to make an query for a non blob column.
My table structure is :
REATE TABLE t_testblob (
filename VARCHAR2(30) DEFAULT NULL NULL,
apkdata BLOB NULL
)
I found an exemple on insert and update :
INSERT INTO table_name VALUES (memberlist,?,memberlist)
UPDATE table_name SET ImageFieldName = ? WHERE ID=yourId
But these structure of querys or abstract to me . What should memberlist be ? why is there "?" where are the values to be inserted ?
Those question marks means that it is PreparedStatement. Such statements are good for both server and client. Server has less work because it is easier to parse such statement, and client do not need to worry about SQLInjection. Client prepares such query, builds buffer for input values and calls it.
Also such statement is executed very quick compared to "normal" queries, especially in loops, importing data from csv file etc.
I don't know what ODBC C++ library you use while ODBC is strictly C library. Other languages like Java or Python can use it too. I think the easiest is example in Python:
cursor = connection.cursor()
for txt in ('a', 'b', 'c'):
cursor.execute('SELECT * FROM test WHERE txt=?', (txt,))
Of course such PreparedStatement can be used in INSERT or UPDATE statements too, and for your example it can look like:
cursor.execute("INSERT INTO t_testblob (filename, apkdata) VALUE (?, ?)", filename, my_binary_data)

Get column names in sqlite3

I would like to print my sql table contents and for that reason, I would like to retrieve the column name from the table. One solution I came across was :
SELECT sql FROM sqlite_master WHERE tbl_name = 'table_name' AND type = 'table'
But looks like I will have to parse the results.
Another suggestion was to use:
PRAGMA table_info(table_name);
but the below sqlite page suggests not to use this :
http://www.sqlite.org/pragma.html#pragma_full_column_names
Does there exists any way to achieve this. Also what would be the syntax to use
PRAGMA table_info(table_name);
Above solutions have been taken from here
Since your question is tagged c I assume you have access to the SQLite C API. If you create a prepared statement with one of the prepare_v2 functions that selects from the table you want you can use sqlite3_column_name to get the name of each column.
You can safely use PRAGMA table_info(table-name); since it's not deprecated in any way (yours post links to another pragma).
int sqlite3_get_table(
sqlite3 *db, /* An open database */
const char *zSql, /* SQL to be evaluated */
char ***pazResult, /* Results of the query */
int *pnRow, /* Number of result rows written here */
int *pnColumn, /* Number of result columns written here */
char **pzErrmsg /* Error msg written here */
);
If you are using c/c++, you can use the function sqlite3_get_table(db, query, result, nrow, ncol, errmsg);
Make the query as select * from table;
And the first few results result[0], result[1]...... will have the column names.
This setting will toggle showing column names as part of the return for select statements:
.headers on