Swift - SQLite connection code explanation - c

I am currently trying to connect my iOS app with SQLite database I have on my computer locally, and while I have found a series of codes that work, I would like to understand how they work.
The below is from the post
Use sqlite3_exec to perform SQL (e.g. create table).
if sqlite3_exec(db, "create table if not exists test (id integer primary key autoincrement, name text)", nil, nil, nil) != SQLITE_OK {
let errmsg = String.fromCString(sqlite3_errmsg(db))
print("error creating table: \(errmsg)")
}
I understand the if statement but why is there 3 nils in the parameter and where is sqlite3_exec coming from? is it bundled with C when I imported sqlite3.dylib?
The next line as I understand defines errmsg as a string that displays the error message returned by sqlite3 along with the database name.
var statement: COpaquePointer = nil
if sqlite3_prepare_v2(db, "insert into test (name) values (?)", -1, &statement, nil) != SQLITE_OK {
let errmsg = String.fromCString(sqlite3_errmsg(db))
print("error preparing insert: \(errmsg)")
}
if sqlite3_bind_text(statement, 1, "foo", -1, SQLITE_TRANSIENT) != SQLITE_OK {
let errmsg = String.fromCString(sqlite3_errmsg(db))
print("failure binding foo: \(errmsg)")
}
if sqlite3_step(statement) != SQLITE_DONE {
let errmsg = String.fromCString(sqlite3_errmsg(db))
print("failure inserting foo: \(errmsg)")
}
The above also raises some questions for me.
What is COpaquePointer? is it just a pointer?
Again the same questions about the parameter of the first statement (db, "insert into test (name) values (?)", -1, &statement, nil) Why is there -1 or nil?
And is SQLITE_OK returned by SQLite?

Related

How to insert data to SQLServer with GORM by using *DB.Create()

I find two ways to insert data into SQLServer with GORM.
GORM.DB.Exec("insert into [tableA] (value1,value2) VALUES (?,?)",v1,v2). It works.
GROM.DB.Create(&myDataStruct).Error. This reports Error and message is "LastInsertId is not supported. Please use the OUTPUT clause or add select ID = convert(bigint, SCOPE_IDENTITY()) to the end of your query."
I understand what's the means of this instruction, but I don't know how to code.
Thanks for any help.
following my code
db := mssql.GetMssqlDB()
defer db.Close()
newData := mssql.People{
Name: "Tom",
Age: 12,
}
err := db.Create(&newData).Error
if err != nil {
fmt.Println()
}
and data struct
type People struct {
ID int64 `gorm:"primary_key;column:id"`
Name string `gorm:"column:name"`
Age int `gorm:"column:age"`
}
func (p People) TableName() string {
return "dbo.people"
}
This is a bug and have been fixed since https://github.com/go-gorm/gorm/pull/2690.
If you get the same error, try update your gorm and it should work.

How to use 'where id in' clauses with jackc/pgx?

Does pgx offer any support for 'where in' clauses? I found in another stackoverflow thread that one should use string concatenation to build the query manually. IMO this is a bit error prone though, as you have to take care of escaping/sql injection and the like on your own.
I also tried to figure it out on my own:
const updatePurgedRecordingsStmt = "update recordings set status = 'DELETED', deleted = now() where status <> 'DELETED' and id in ($1);"
func (r *Repository) DeleteRecordings() error {
pool, err := r.connPool()
if err != nil {
return errors.Wrap(err, "cannot establish connection")
}
pgRecIds := &pgtype.Int4Array{}
if err := pgRecIds.Set([]int32{int32(1), int32(2)}); err != nil {
return errors.Wrap(err, "id conversion failed")
}
if _, err = pool.Exec(updatePurgedRecordingsStmt, pgRecIds); err != nil {
return errors.Wrap(err, "update stmt failed")
}
return nil
}
When I execute this code, I get the following error though:
ERROR: incorrect binary data format in bind parameter 1 (SQLSTATE 22P03)
The versions I am using:
Postgres:
db=> SELECT version();
version
-----------------------------------------------------------------------------------------------------------
PostgreSQL 9.6.11 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.2 20140120 (Red Hat 4.8.2-16), 64-bit
(1 row)
PGX:
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90=
github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
As you already know IN expects a list of scalar expressions, not an array, however pgtype.Int4Array represents an array, not a list of scalar expressions.
"IMO this is a bit error prone though, as you have to take care of escaping/sql injection and the like on your own. "
Not necessarily, you can loop over your array, construct a string of parameter references, concatenate that to the query and then execute it passing in the array with ....
var paramrefs string
ids := []interface{}{1,2,3,4}
for i, _ := range ids {
paramrefs += `$` + strconv.Itoa(i+1) + `,`
}
paramrefs = paramrefs[:len(paramrefs)-1] // remove last ","
query := `UPDATE ... WHERE id IN (` + paramrefs + `)`
pool.Exec(query, ids...)
Alternatively you can use ANY instead of IN.
ids := &pgtype.Int4Array{}
ids.Set([]int{1,2,3,4})
query := `UPDATE ... WHERE id = ANY ($1)`
pool.Exec(query, ids)
(here you may have to cast the param reference to the appropriate array type, I'm not sure, give it a try without cast, if not ok, try with cast)
func prepareWhereINString(count int) string {
var paramrefs string
for i := 0; i < count; i++ {
paramrefs += `$` + strconv.Itoa(i+1) + `,`
}
paramrefs = paramrefs[:len(paramrefs)-1] // remove last ","
return paramrefs
}

Go cannot read last nvarchar(max) value from MS SQL 2014

I have the following query:
SELECT ... ,
grade as [grade],
grade as [grade2]
FROM dbo.[qc_runs] r
JOIN ...
WHERE ...
I send it to MS SQL Server 2014 from my Go code and want to get back data (I am using github.com/denisenkom/go-mssqldb driver). However, I can read first grade value (type nvarchar(max)), but second one arrives empty! These are the same table fields, just duplicated. If I delete first grade value from query and leave just one, it will still arrive empty! The column is described as following:
[grade] [nvarchar](max) NULL,
SQL Management Studio executes this query just fine, both grade values are not empty, but Go code doesn't!
UPDATE #1
Go code:
evaluations, err := db.Query("EXEC qa.dbo.sp_get_evaluation_list ?", uid)
if err != nil {
if err == sql.ErrNoRows {
return list, nil
}
return list, err
}
// Get column names
colNames, err := evaluations.Columns()
if err != nil {
log.Logf("Failed to get columns: %v", err)
return list, err
}
// Result is your slice string.
readCols := make([]interface{}, len(colNames))
// Read data
for evaluations.Next() {
writeCols := make([]string, len(colNames))
for i := range writeCols {
readCols[i] = &writeCols[i]
}
evaluations.Scan(readCols...)
for i := range writeCols {
fmt.Println(colNames[i], ": ", writeCols[i])
}
...
}
Output:
...
grade : <some text from DB>
grade2 :
I'm not a go programmer. But you need to name those fields differently. How could your code possible discern between the first and second [grade]?
SELECT ... ,
grade as [grade],
grade as [grade2]
FROM dbo.[qc_runs] r
JOIN ...
WHERE ...
Given that you're getting an empty value on the second read, I suspect that the go driver (and the data reader you're using from it) is one-way only.
Problem solved, evaluations.Scan(readCols...) returned error err sql: Scan error on column index 3: unsupported Scan, storing driver.Value type <nil> into type *string, and left the rest of array blank

Executing SQL query in Golang

I've seen two ways of people executing queries using Golang builtin database/sql queries. One of them is using fmt.Sprintf:
func (db *DB) CreateUserTable() (sql.Result, error) {
statement := "CREATE TABLE %s (%s, %s, %s, %s, %s)"
v := []interface{}{"User", "ID int PRIMARY KEY NOT NULL", "Name varchar(100) UNIQUE", "Email varchar(100) UNIQUE", "Address varchar(100) ", "Username varchar(100) UNIQUE"}
return db.Exec(fmt.Sprintf(statement, v...))
}
and the other one is using prepared statement:
func (db *DB) CreateUserTable() (sql.Result, error) {
statement, err := db.Prepare("INSERT INTO User(tbl1,tbl2,tbl3) VALUES(?,?,?)")
if err != nil {
log.Fatal(err)
}
return statement.Exec("value1", "value2", "value3")
}
The first gives benefit by enabling you to dynamically set the table name, column name, and the values. But the second one only for values. What's the difference? Which one should I use?
Never build SQL from strings that come from outside your system.
Always use the ? syntax.
If you must set SQL parts like table names, prepare multiple, complete SQL statements that contain ? for the values. Select the SQL to execute, maybe based on user input, but never build SQL from user input.
It is cleaner to use prepared statements so that whenever a requirement changes you can easily modify the statements. Also to prevent SQL injections.
Prepared statements is much better than concatenating strings, for all
the usual reasons (avoiding SQL injection attacks, for example).
In MySQL, the parameter placeholder is ?, and in PostgreSQL it is $N,
where N is a number. SQLite accepts either of these.
One more thing is Prepared statements can be used for repetitive approach, can be executed multiple times and can be destroyed.
stmt, err := db.Prepare("select id, name from users where id = ?")
if err != nil {
log.Fatal(err)
}
defer stmt.Close() // closing the statement
rows, err := stmt.Query(1)
And you are using interfaces
func (db *DB) CreateUserTable() (sql.Result, error) {
statement := "CREATE TABLE %s (%s, %s, %s, %s, %s)"
v := []interface{}{"User", "ID int PRIMARY KEY NOT NULL", "Name varchar(100) UNIQUE", "Email varchar(100) UNIQUE", "Address varchar(100) ", "Username varchar(100) UNIQUE"}
return db.Exec(fmt.Sprintf(statement, v...))
}
which can take any type of parameter under the hood which can be vulnerable
For more detailed information Go for this Link

Golang mssql driver returns "mssql: Invalid object name"

In an application I have a globally scoped
var db *sql.DB
that is later called with
slcstrSource, slcint64Timestamp, slcstrContent, err := DB_functions.GetContent(db)
if err != nil {
fmt.Println("Error: " + err.Error())
}
GetContent is this:
func GetContent(db *sql.DB) ([]string, []int64, []string, error) {
var slcstrContent []string
var slcint64Timestamp []int64
var slcstrSource []string
// Run the query
rows, err := db.Query("SELECT source, timestamp, content FROM MyDatabase.MyTable")
if err != nil {
return slcstrSource, slcint64Timestamp, slcstrContent, err
}
defer rows.Close()
for rows.Next() {
// Holding variables for the content in the columns
var source, content string
var timestamp int64
// Get the results of the query
err := rows.Scan(&source, &timestamp, &content)
if err != nil {
return slcstrSource, slcint64Timestamp, slcstrContent, err
}
// Append them into the slices that will eventually be returned to the caller
slcstrSource = append(slcstrSource, source)
slcstrContent = append(slcstrContent, content)
slcint64Timestamp = append(slcint64Timestamp, timestamp)
}
return slcstrSource, slcint64Timestamp, slcstrContent, nil
}
When I run the application and these sections of code are hit, I get an:
Error: mssql: Invalid object name 'MyDatabase.MyTable'.
When I db.Ping() the database, it seems to work. From what I've narrowed down the error is happening right at the query, but I can't find what's wrong. I checked the database and there is a database called MyDatabase with a table called MyTable and the table has information in those three columns...
Is there something I'm missing before making the query, or in making the query?
I checked the database and there is a database called MyDatabase with
a table called MyTable and the table has information in those three
columns...
It seems like the driver is working just like it should. In order to query a table in SQL Server you should use [Database].[Schema].[TableName]. If you have not defined a particular schema name for your table then it will be created under the dbo schema by default.
In saying that you don't really need to specify the database name in your query. You rather define that on the connection string. I'm not sure how you have defined your connection details but have a look at the below and adapt accordingly to your needs.
var (
debug = flag.Bool("debug", false, "enable debugging")
password = flag.String("password", "mypwd", "the database password")
port *int = flag.Int("port", 1433, "the database port")
server = flag.String("server", "MyServer", "the database server")
user = flag.String("user", "MyUser", "the database user")
connStr = fmt.Sprintf("server=%s;Initial Catalog=MySchema;userid=%s;password=%s;port=%d", *server, *user, *password, *port)
)
func main() {
db, err := sql.Open("mssql", connStr)
}
Then you can query your table like this:
rows, err := db.Query("SELECT source, timestamp, content FROM MySchema.MyTable")

Resources