Stored procedures with ODBC through Go/Golang - stored-procedures

I'm using the ODBC driver from The Brainman. I'm using the generic ODBC syntax, which is to use "CALL" to invoke a Stored Procedure. Here is my code:
stmt, stmtErr := db.Prepare("CALL RecordClick (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
if stmtErr != nil {
fmt.Printf("\nstmtErr: %s", stmtErr)
}
defer stmt.Close()
var aclickid int
stmtRows, stmtRowsErr := stmt.Query(xaid, subtag, r.Referer, requestUserAgent, requestIP, ip, ua, title, description, displayurl, clickUrl, kw, rpc, exid)
if stmtRowsErr != nil {
fmt.Printf("\nstmtRowsErr: %s", stmtRowsErr)
}
for stmtRows.Next() {
stmtRows.Scan(&aclickid)
}
When I run this, I get the following error:
stmtRowsErr: sql: converting Exec argument #2's type: unsupported type func() string, a func
I really don't understand what I'm doing wrong here. I've tried it with and without curly braces around the CALL XXX (?, ?, ?) part, and it still won't work. Any ideas?
Thanks.

My guess is that r.Referer (Type func) in stmt.Query should be r.Referer() (Returns type string, From net/http).
So the line should read:
stmtRows, stmtRowsErr := stmt.Query(xaid, subtag, r.Referer(), requestUserAgent, requestIP, ip, ua, title, description, displayurl, clickUrl, kw, rpc, exid)
That error is coming from Line #37 of convert.go in the database/sql package.
It's telling you that the type conversion for argument #2 (zero indexed) of the SQL query couldn't be converted into the type expected by the SQL engine (In particular the function driver.DefaultParameterConverter.ConvertValue(arg) failed.)
Check the types for each variable in your query with something like:
fmt.Printf("%T %T %T %T %T %T %T %T %T %T %T %T %T %T", xaid, subtag, r.Referer, requestUserAgent, requestIP, ip, ua, title, description, displayurl, clickUrl, kw, rpc, exid)
and see if any of them are unexpected or incorrect.

Related

functional construct for flattening array for multiple row insert sql query

Is there a way to generate multiple row sql query (values only) using some functional constructs of array?
I've an array of Roles that I want to insert into sqlite database.
struct Role {
var id: Int32
var name: String?
}
func updateByUserId(_ id: Int32, _ roles: [Role]) {
let sql = "INSERT INTO user_role(user_id, role_id) VALUES( \(id), \(roles.map..) )"
}
Expectation:
for instances if id is 1 and roles has an array [10, 11, 14, 15]
Generated SQL should be
INSERT INTO user_role(user_id, role_id) VALUES(1, 10), (1, 11), (1, 14), (1, 15)
SQL Syntax for Multiple row insert is
INSERT INTO MyTable ( Column1, Column2 ) VALUES(Value1, Value2),
(Value1, Value2)
You can map each role to the string (id, role), then join the array of strings with the separator ,:
let values = roles.map { "(\(id), \($0))" }.joined(separator: ", ")
let sql = "INSERT INTO user_role(user_id, role_id) VALUES\(values)"
Although for this particular scenario the SQL string computation is not problematic, it's good practice to use parametrized statements for every DB query.
Working exclusively with parametrized statements avoids vulnerabilities like SQL injection, or malformed queries that fail to execute (when dealing with strings instead of ints).
So, I'd recommend going via the above route by writing something like this:
func updateByUserId(_ id: Int32, _ roles: [Role]) -> (statement: String, params: [Int32]) {
let statement = "INSERT INTO user_role(user_id, role_id) VALUES " + Array(repeating: "(?, ?)", count: roles.count).joined(separator: ", ")
let params = roles.flatMap { [id, $0.id] }
return (statement, params)
}
For your example in the question, the output would be something like this:
(statement: "INSERT INTO user_role(user_id, role_id) VALUES (?, ?), (?, ?), (?, ?), (?, ?)", params: [1, 10, 1, 11, 1, 14, 1, 15])
You can then use the SQLite functions to create the parametrized statement and bind the given values to it.
P.S. There is also the matter of validating that the array of roles is not empty, in which case you'd get an invalid SQL as output. To handle this, you can make the function return an optional, and nil will signal an empty array. Doing this will enable a small performance improvement, as you'll be able to use String(repeating:count:), which is a little bit faster than creating an array and joing it later on:
func updateByUserId(_ id: Int32, _ roles: [Role]) -> (statement: String, params: [Int32])? {
guard !roles.isEmpty else { return nil }
return (statement: "INSERT INTO user_role(user_id, role_id) VALUES (?, ?)" + String(repeating: ", (?, ?)", count: roles.count - 1),
params: roles.flatMap { [id, $0.id] })
}

Typeorm: Execute raw query with parameters

I'm trying to execute raw query in typeorm with parameters
I tried following queries:
insert into data(id, name, gender) values(?, ?,?)
insert into data(id, name, gender) values($1, $2, $3)
insert into data(id, name, gender) values(:id, :name, :gender)
The typeorm code is:
import { getManager } from 'typeorm';
await getManager().query(query, [1, 'test', 'male']);
What is wrong? Is there any other way?
Issue solved with this link. It depends on the underlying database which syntax to use.
https://github.com/typeorm/typeorm/issues/881
as #ashutosh said, it depends on the driver of your database
For mysql/mysql2 you should use ? as placehoder. For example
manager.query('SELECT id FROM foos WHERE createdAt > ? AND id > ?', [new Date(), 3])
For oracle, you can use something like this for me like this :
const querySingleValue = SELECT * FROM TABLE1 WHERE name in (:param) ;
string value :
getManager().query(querySingleValue,[param]) ;

how to insert new row by using executeUpdate - FMdatabase

I'm using fmdb library.
https://github.com/ccgus/fmdb
I would like to insert new records but I don't know how can I do:
if let db = database, let q = db.executeUpdate("insert into \(table) (catid,subcat_id,id,subcat_title,title,description,lat,lon,takhfif,images,wifi,apple_health,wc,full_time,pos,work_hours,phone,mobile,fax,website,email,address,facebook,instagram,linkedin,telegram,googleplus,twitter,publish,feature,manager,city,rating_sum,rate_count,lastip,parking,isMallID,mallID,discount_images,price_images,newProduct_images,services_images,order_online,out_upon,cat_title,cat_icon,last_modify,item_logo,cat_logo,rate_sum1,rate_sum2,rate_sum3,rate_count1,rate_count2,rate_count3,rate_title1,rate_title2,rate_title3,rate_enable) values (\(catid),\(subcat_id),\(id),'\(subcat_title)','\(title)','\(description)','\(lat)','\(lon)','\(takhfif)','\(images)',\(wifi),\(apple_health),\(wc),\(full_time),\(pos),'\(work_hours)','\(phone)','\(mobile)','\(fax)','\(website)','\(email)','\(address)','\(facebook)','\(instagram)','\(linkedin)','\(telegram)','\(googleplus)','\(twitter)',\(publish),\(feature),'\(manager)','\(city)',\(rating_sum),\(rate_count),'\(lastip)',\(parking),\(isMallID),\(mallID),'\(discount_images)','\(price_images)','\(newProduct_images)','\(services_images)',\(order_online),\(out_upon),'\(cat_title)','\(cat_icon)',\(last_modify),'\(item_logo)','\(cat_logo)',\(rate_sum1),\(rate_sum2),\(rate_sum3),\(rate_count1),\(rate_count2),\(rate_count3),'\(rate_title1)','\(rate_title2)','\(rate_title3)',\(rate_enable))") {
}
but I got this message before building:
cannot invoke executeUpdate with an argument list of type string
You used the api wrong. The first parameter is the sql, each value has to be represented with an "?". The second one (values) is an array of the values. Try this:
if let db = database, let q = db.executeUpdate(
"insert into \(table) (catid,subcat_id,id,subcat_title,title,description,lat,lon,takhfif,images,wifi,apple_health,wc,full_time,pos,work_hours,phone,mobile,fax,website,email,address,facebook,instagram,linkedin,telegram,googleplus,twitter,publish,feature,manager,city,rating_sum,rate_count,lastip,parking,isMallID,mallID,discount_images,price_images,newProduct_images,services_images,order_online,out_upon,cat_title,cat_icon,last_modify,item_logo,cat_logo,rate_sum1,rate_sum2,rate_sum3,rate_count1,rate_count2,rate_count3,rate_title1,rate_title2,rate_title3,rate_enable) values (?, ?, ? ...., ?, ?, ? )"
values: [catid, subcat_id, id, subcat_title, title, description, lat, lon, takhfif, images, wifi, apple_health, wc, full_time, pos, work_hours, phone, mobile, fax, website, email, address, facebook, instagram, linkedin, telegram, googleplus, twitter, publish, feature, manager, city, rating_sum, rate_count, lastip, parking, isMallID, mallID, discount_images, price_images, newProduct_images, services_images, order_online, out_upon, cat_title, cat_icon, last_modify, item_logo, cat_logo, rate_sum1, rate_sum2, rate_sum3, rate_count1, rate_count2, rate_count3, rate_title1, rate_title2, rate_title3, rate_enable]) {
}
Note: I didn't count the values, so you have to ensure that the count of "?" inside of the string is exactly the count of your values!
Maybe you should think about a function to improve the readability of your code. Something like that:
extension FMDatabase {
func insert(into table: String, row values: [(column: String, content: Any)]) {
guard (values.count > 0) else { fatalError(values.count > 0, "Inserting nothing into a table makes no sense!") }
var sql = "INSERT INTO \(table) ("
var contents: [Any] = []
for (index, value) in values.enumerated() {
sql += index == 0 ? value.column : ", \(value.column)"
contents.append(value.content)
}
sql += ") VALUES ("
for index in 0..<values.count {
sql += index == 0 ? "?" : ", ?"
}
sql += ")"
executeUpdate(sql, values: contents)
}
}
Then you can insert values like that:
if let db = database {
db.insert(
into: table,
row: [
(column: "catid", content: catid),
(column: "subcat_id", content: subcat_id),
...
(column: "rate_enable", content: rate_enable)
]
)
}
Isn't it better readable?

Platform Core Event Bus throwing DataIntegrationException

I'm using the plugin Platform Core and the module of Event Bus. I need to add some domains assync, but i force sync to test, in my database but de exception DataIntegrityViolation is throwed everytime i tried to save(). That is the domain structure:
class Alocacao {
static belongsTo = [tipoUnidade: TipoUnidade]
//Anothers attrs and constraints
}
class TipoUnidade {
static belongsTo = [ estabelecimento : Estabelecimento];
static hasMany = [alocacoes: Alocacao];
List<Alocacao> alocacoes
//Constraints and fields...
}
In my controller i have this code:
tipoUnidadeService.save(tipoUnidadeInstance)
event('criarAlocacaoQuartoEvent', tipoUnidadeInstance, [fork: false])
And in my event listener this:
class AlocacaoService {
#Listener(topic="criarAlocacaoQuartoEvent", namespace="app")
def defaultAlocacaoCreator(EventMessage<TipoUnidade> message) {
Canal canalSist = Canal.findByNomeIlike("%manual%")
TipoUnidade quarto = TipoUnidade.get(message.data.id)
def alocacaoSistema = new Alocacao(exclusivo: 0, nExclusivo: 0, data: tmp.toDate(), canal: canalSist, tipoUnidade: quarto).save(failOnError: true) //EXCEPTION!!
}
}
I don't know what's happen with that code.
This is the entire exception:
util.JDBCExceptionReporter SQL Error: 1048, SQLState: 23000
util.JDBCExceptionReporter Column 'tipo_unidade_id' cannot be null
services.AlocacaoService could not insert: [br.com.qreserva.portal.domains.Alocacao]; SQL [insert into alocacao (version, canal_id, data, date_created, exclusivo, n_exclusivo, tipo_unidade_id, alocacoes_idx) values (?, ?, ?, ?, ?, ?, ?, ?)]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not insert: [br.com.qreserva.portal.domains.Alocacao]
org.springframework.dao.DataIntegrityViolationException: could not insert: [br.com.qreserva.portal.domains.Alocacao]; SQL [insert into alocacao (version, canal_id, data, date_created, exclusivo, n_exclusivo, tipo_unidade_id, alocacoes_idx) values (?, ?, ?, ?, ?, ?, ?, ?)]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not insert: [br.com.qreserva.portal.domains.Alocacao]
at br.com.qreserva.portal.services.AlocacaoService.defaultAlocacaoCreator(AlocacaoService.groovy:178)
at org.grails.plugin.platform.events.registry.DefaultEventsRegistry$ListenerHandler.invoke(DefaultEventsRegistry.java:238)
at org.grails.plugin.platform.events.registry.DefaultEventsRegistry.invokeListeners(DefaultEventsRegistry.java:160)
at org.grails.plugin.platform.events.publisher.DefaultEventsPublisher.event(DefaultEventsPublisher.java:79)
at org.grails.plugin.platform.events.EventsImpl.event(EventsImpl.groovy:154)
at org.grails.plugin.platform.events.EventsImpl$_closure1_closure5_closure9.doCall(EventsImpl.groovy:83)
at br.com.qreserva.portal.controllers.TipoUnidadeController.salvar(TipoUnidadeController.groovy:126)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [br.com.qreserva.portal.domains.Alocacao]
... 10 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'tipo_unidade_id' cannot be null
at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
at com.mysql.jdbc.Util.getInstance(Util.java:381)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1936)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2060)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2019)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1937)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1922)
at com.jolbox.bonecp.PreparedStatementHandle.executeUpdate(PreparedStatementHandle.java:203)
... 10 more
[EDIT]
I create this git project to illustrate my problem and the same errors occurs
[SOLUTION]
I solved this using the addTo* attrs in belongsTo class. But why this works i have no idea.
The core problem is
MySQLIntegrityConstraintViolationException: Column 'tipo_unidade_id' cannot be null
That column is the foreign key for the tipoUnidade variable (the camel-case tipoUnidade becomes tipo_unidade as a column name, and has the _id suffix added because it's a FK) so that implies that the value there is null, meaning that TipoUnidade.get(message.data.id) returned null. This will happen if message.data.id is null (possibly because message.data is not null but not saved yet?), or if there's no record for that id.
Since you're working with async code, my guess is that it's a timing issue, and the message.data value isn't saved yet, but you're trying to load a value based on it having been inserted.
Check the values, and consider turning on SQL logging so you can see the insertion and retrieval order for the items.
I have the same issue with EventBus. The problem is that EventBus works with threads. I suppose you use Transactions too. The both objects were created in two different threads and hibernate can't can't save one of them because the other is not available.
Based on response of Burt i make a workaround and for some reason using the addTo* resolve the problem.
TipoUnidade quarto = TipoUnidade.get(message.data.id)
quarto.addToAlocacoes(new Alocacao(exclusivo: 0, nExclusivo: 0, data: tmp.toDate(), canal: canalSist))
quarto.save(failOnError: true)
Thx.

postgresql with jdbc and stored procedures (functions): ResultSet

I just tried to call a stored function from the server (getStat), that is looking like this:
create type stat as (type text, location text, number int);
create function getStat() returns setof stat as 'select distinct table1.type, table1.location, table1.number from table1, table2 where table2.finding=10 order by number desc;' language 'sql';
Now here is the jdbc code:
CallableStatement callable = null;
String storedProc = "{call getStat(?, ?, ?)}";
try {
callable = connection.prepareCall(storedProc);
callable.registerOutParameter(1, java.sql.Types.VARCHAR);
callable.registerOutParameter(2, java.sql.Types.VARCHAR);
callable.registerOutParameter(3, java.sql.Types.INTEGER);
boolean results = callable.execute();
System.out.println(callable.getString(1));
System.out.println(callable.getString(2));
System.out.println(callable.getInt(3));
while(results){
ResultSet rs = callable.getResultSet();
while(rs.next()){
System.out.println(rs.getString(1));
System.out.println(rs.getString(2));
System.out.println(rs.getInt(3));
}
//rs.close();
results = callable.getMoreResults();
}
Okay, and now the problem:
When I am calling it, it just prints out the first line, of the whole bulk, that should be printend. Yeah, that is clear, because I execute the following code:
System.out.println(callable.getString(1));
System.out.println(callable.getString(2));
System.out.println(callable.getInt(3));
But I do the same in the while loop...and nothing more is displayed.
Maybe the problem is something obvious, but I am missing that :(
Thanks!
No need to use a CallableStatement with the {call ...} syntax.
Just use a select and a regular Statement:
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("select * from getStat()");
while (rs.next())
{
System.out.println(rs.getString(1));
System.out.println(rs.getString(2));
System.out.println(rs.getInt(3));
}

Resources