Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,537 @@
|
||||
module pg
|
||||
|
||||
import orm
|
||||
import time
|
||||
import net.conv
|
||||
|
||||
// ---- ORM on Conn (single pinned connection) ----
|
||||
|
||||
// select is used internally by V's ORM for processing `SELECT ` queries.
|
||||
pub fn (c &Conn) select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
|
||||
c.ensure_active()!
|
||||
where_with_tenant := orm.apply_tenant_filter(config.table, where)
|
||||
query := orm.orm_select_gen(config, '"', true, '$', 1, where_with_tenant)
|
||||
|
||||
rows := pg_stmt_worker(c, query, where_with_tenant, data)!
|
||||
|
||||
mut ret := [][]orm.Primitive{}
|
||||
|
||||
for row in rows {
|
||||
mut row_data := []orm.Primitive{}
|
||||
for i, val in row.vals {
|
||||
row_data << val_to_primitive(val, config.types[i])!
|
||||
}
|
||||
ret << row_data
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// insert is used internally by V's ORM for processing `INSERT ` queries.
|
||||
pub fn (c &Conn) insert(table orm.Table, data orm.QueryData) ! {
|
||||
c.ensure_active()!
|
||||
query, converted_data :=
|
||||
orm.orm_stmt_gen(.pg, table, '"', .insert, true, '$', 1, data, orm.QueryData{})
|
||||
pg_stmt_worker(c, query, converted_data, orm.QueryData{})!
|
||||
}
|
||||
|
||||
// update is used internally by V's ORM for processing `UPDATE ` queries.
|
||||
pub fn (c &Conn) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
|
||||
c.ensure_active()!
|
||||
where_with_tenant := orm.apply_tenant_filter(table, where)
|
||||
query, _ := orm.orm_stmt_gen(.default, table, '"', .update, true, '$', 1, data,
|
||||
where_with_tenant)
|
||||
pg_stmt_worker(c, query, data, where_with_tenant)!
|
||||
}
|
||||
|
||||
// delete is used internally by V's ORM for processing `DELETE ` queries.
|
||||
pub fn (c &Conn) delete(table orm.Table, where orm.QueryData) ! {
|
||||
c.ensure_active()!
|
||||
where_with_tenant := orm.apply_tenant_filter(table, where)
|
||||
query, _ := orm.orm_stmt_gen(.default, table, '"', .delete, true, '$', 1, orm.QueryData{},
|
||||
where_with_tenant)
|
||||
pg_stmt_worker(c, query, orm.QueryData{}, where_with_tenant)!
|
||||
}
|
||||
|
||||
// last_id is used internally by V's ORM for post-processing `INSERT ` queries.
|
||||
pub fn (c &Conn) last_id() int {
|
||||
query := 'SELECT LASTVAL();'
|
||||
|
||||
return c.q_int(query) or { 0 }
|
||||
}
|
||||
|
||||
// create is used internally by V's ORM for processing table creation queries (DDL).
|
||||
pub fn (c &Conn) create(table orm.Table, fields []orm.TableField) ! {
|
||||
query := orm.orm_table_gen(.pg, table, '"', true, 0, fields, pg_type_from_v, false) or {
|
||||
return err
|
||||
}
|
||||
stmts := query.split(';')
|
||||
for stmt in stmts {
|
||||
if stmt != '' {
|
||||
c.exec(stmt + ';')!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// drop is used internally by V's ORM for processing table destroying queries (DDL).
|
||||
pub fn (c &Conn) drop(table orm.Table) ! {
|
||||
query := 'DROP TABLE "${table.name}";'
|
||||
c.exec(query)!
|
||||
}
|
||||
|
||||
// orm_begin starts a transaction on this conn.
|
||||
pub fn (c &Conn) orm_begin() ! {
|
||||
c.begin_on_conn()!
|
||||
}
|
||||
|
||||
// orm_commit commits the transaction on this conn.
|
||||
pub fn (c &Conn) orm_commit() ! {
|
||||
c.commit()!
|
||||
}
|
||||
|
||||
// orm_rollback rolls back the transaction on this conn.
|
||||
pub fn (c &Conn) orm_rollback() ! {
|
||||
c.rollback()!
|
||||
}
|
||||
|
||||
// orm_savepoint creates a savepoint on this conn.
|
||||
pub fn (c &Conn) orm_savepoint(name string) ! {
|
||||
c.savepoint(name)!
|
||||
}
|
||||
|
||||
// orm_rollback_to rolls back to a savepoint on this conn.
|
||||
pub fn (c &Conn) orm_rollback_to(name string) ! {
|
||||
c.rollback_to(name)!
|
||||
}
|
||||
|
||||
// orm_release_savepoint releases a savepoint on this conn.
|
||||
pub fn (c &Conn) orm_release_savepoint(name string) ! {
|
||||
c.release_savepoint(name)!
|
||||
}
|
||||
|
||||
// ---- ORM on DB (acquire-use-release per call) ----
|
||||
|
||||
// select acquires a conn from the pool and runs the ORM SELECT on it.
|
||||
pub fn (mut db DB) select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
|
||||
mut c := db.pool.acquire()!
|
||||
defer {
|
||||
c.close() or {}
|
||||
}
|
||||
return c.select(config, data, where)
|
||||
}
|
||||
|
||||
// insert acquires a conn from the pool, runs the ORM INSERT on it, and
|
||||
// stashes LASTVAL() captured on the same session for the calling thread.
|
||||
// V's `sql db { insert ... }` macro emits a follow-up `db.last_id()` call;
|
||||
// stashing here lets that read return the correct id even though the pool
|
||||
// may hand out a different conn for the second call.
|
||||
pub fn (mut db DB) insert(table orm.Table, data orm.QueryData) ! {
|
||||
mut c := db.pool.acquire()!
|
||||
defer {
|
||||
c.close() or {}
|
||||
}
|
||||
c.insert(table, data)!
|
||||
db.pool.stash_last_id(c.last_id())
|
||||
}
|
||||
|
||||
// update acquires a conn from the pool and runs the ORM UPDATE on it.
|
||||
pub fn (mut db DB) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
|
||||
mut c := db.pool.acquire()!
|
||||
defer {
|
||||
c.close() or {}
|
||||
}
|
||||
c.update(table, data, where)!
|
||||
}
|
||||
|
||||
// delete acquires a conn from the pool and runs the ORM DELETE on it.
|
||||
pub fn (mut db DB) delete(table orm.Table, where orm.QueryData) ! {
|
||||
mut c := db.pool.acquire()!
|
||||
defer {
|
||||
c.close() or {}
|
||||
}
|
||||
c.delete(table, where)!
|
||||
}
|
||||
|
||||
// create acquires a conn from the pool and runs CREATE TABLE on it.
|
||||
pub fn (mut db DB) create(table orm.Table, fields []orm.TableField) ! {
|
||||
mut c := db.pool.acquire()!
|
||||
defer {
|
||||
c.close() or {}
|
||||
}
|
||||
c.create(table, fields)!
|
||||
}
|
||||
|
||||
// drop acquires a conn from the pool and runs DROP TABLE on it.
|
||||
pub fn (mut db DB) drop(table orm.Table) ! {
|
||||
mut c := db.pool.acquire()!
|
||||
defer {
|
||||
c.close() or {}
|
||||
}
|
||||
c.drop(table)!
|
||||
}
|
||||
|
||||
// last_id returns the id stashed by this thread's most recent `DB.insert`
|
||||
// (or 0 if there is none). LASTVAL() itself is session-scoped, so calling
|
||||
// it on a freshly-checked-out pool conn would return the wrong value or 0;
|
||||
// `DB.insert` captures it on the same conn that ran the INSERT and stashes
|
||||
// it per-thread, which is what V's ORM macro expects.
|
||||
pub fn (mut db DB) last_id() int {
|
||||
return db.pool.take_last_id()
|
||||
}
|
||||
|
||||
// ---- ORM on Tx (use the pinned conn) ----
|
||||
|
||||
// select runs the ORM SELECT on the pinned transaction conn.
|
||||
pub fn (mut tx Tx) select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
|
||||
tx.ensure_active()!
|
||||
return tx.conn.select(config, data, where)
|
||||
}
|
||||
|
||||
// insert runs the ORM INSERT on the pinned transaction conn.
|
||||
pub fn (mut tx Tx) insert(table orm.Table, data orm.QueryData) ! {
|
||||
tx.ensure_active()!
|
||||
tx.conn.insert(table, data)!
|
||||
}
|
||||
|
||||
// update runs the ORM UPDATE on the pinned transaction conn.
|
||||
pub fn (mut tx Tx) update(table orm.Table, data orm.QueryData, where orm.QueryData) ! {
|
||||
tx.ensure_active()!
|
||||
tx.conn.update(table, data, where)!
|
||||
}
|
||||
|
||||
// delete runs the ORM DELETE on the pinned transaction conn.
|
||||
pub fn (mut tx Tx) delete(table orm.Table, where orm.QueryData) ! {
|
||||
tx.ensure_active()!
|
||||
tx.conn.delete(table, where)!
|
||||
}
|
||||
|
||||
// create runs CREATE TABLE on the pinned transaction conn.
|
||||
pub fn (mut tx Tx) create(table orm.Table, fields []orm.TableField) ! {
|
||||
tx.ensure_active()!
|
||||
tx.conn.create(table, fields)!
|
||||
}
|
||||
|
||||
// drop runs DROP TABLE on the pinned transaction conn.
|
||||
pub fn (mut tx Tx) drop(table orm.Table) ! {
|
||||
tx.ensure_active()!
|
||||
tx.conn.drop(table)!
|
||||
}
|
||||
|
||||
// last_id returns the last inserted id on the pinned conn.
|
||||
pub fn (mut tx Tx) last_id() int {
|
||||
if tx.done || isnil(tx.conn) {
|
||||
return 0
|
||||
}
|
||||
return tx.conn.last_id()
|
||||
}
|
||||
|
||||
// orm_begin is a no-op on Tx (begin already ran when the Tx was created).
|
||||
// It exists so Tx satisfies `orm.TransactionalConnection` for nested savepoints.
|
||||
pub fn (mut tx Tx) orm_begin() ! {
|
||||
}
|
||||
|
||||
// orm_commit commits the Tx.
|
||||
pub fn (mut tx Tx) orm_commit() ! {
|
||||
tx.commit()!
|
||||
}
|
||||
|
||||
// orm_rollback rolls back the Tx.
|
||||
pub fn (mut tx Tx) orm_rollback() ! {
|
||||
tx.rollback()!
|
||||
}
|
||||
|
||||
// orm_savepoint creates a savepoint inside the Tx.
|
||||
pub fn (mut tx Tx) orm_savepoint(name string) ! {
|
||||
tx.savepoint(name)!
|
||||
}
|
||||
|
||||
// orm_rollback_to rolls back to a savepoint inside the Tx.
|
||||
pub fn (mut tx Tx) orm_rollback_to(name string) ! {
|
||||
tx.rollback_to(name)!
|
||||
}
|
||||
|
||||
// orm_release_savepoint releases a savepoint inside the Tx.
|
||||
pub fn (mut tx Tx) orm_release_savepoint(name string) ! {
|
||||
tx.release_savepoint(name)!
|
||||
}
|
||||
|
||||
// ---- utils ----
|
||||
|
||||
fn pg_stmt_binder(mut types []u32, mut vals []&char, mut lens []int, mut formats []int, d orm.QueryData) {
|
||||
for data in d.data {
|
||||
pg_stmt_match(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
}
|
||||
|
||||
fn pg_stmt_match_array[T](mut types []u32, mut vals []&char, mut lens []int, mut formats []int, data []T) {
|
||||
for element in data {
|
||||
pg_stmt_match(mut types, mut vals, mut lens, mut formats, orm.Primitive(element))
|
||||
}
|
||||
}
|
||||
|
||||
fn pg_stmt_match(mut types []u32, mut vals []&char, mut lens []int, mut formats []int, data orm.Primitive) {
|
||||
match data {
|
||||
bool {
|
||||
types << u32(Oid.t_bool)
|
||||
vals << &char(&data)
|
||||
lens << int(sizeof(bool))
|
||||
formats << 1
|
||||
}
|
||||
u8 {
|
||||
types << u32(Oid.t_char)
|
||||
vals << &char(&data)
|
||||
lens << int(sizeof(u8))
|
||||
formats << 1
|
||||
}
|
||||
u16 {
|
||||
types << u32(Oid.t_int2)
|
||||
num := conv.hton16(data)
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(u16))
|
||||
formats << 1
|
||||
}
|
||||
u32 {
|
||||
types << u32(Oid.t_int4)
|
||||
num := conv.hton32(data)
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(u32))
|
||||
formats << 1
|
||||
}
|
||||
u64 {
|
||||
types << u32(Oid.t_int8)
|
||||
num := conv.hton64(data)
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(u64))
|
||||
formats << 1
|
||||
}
|
||||
i8 {
|
||||
types << u32(Oid.t_char)
|
||||
vals << &char(&data)
|
||||
lens << int(sizeof(i8))
|
||||
formats << 1
|
||||
}
|
||||
i16 {
|
||||
types << u32(Oid.t_int2)
|
||||
num := conv.hton16(u16(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(i16))
|
||||
formats << 1
|
||||
}
|
||||
int {
|
||||
types << u32(Oid.t_int4)
|
||||
num := conv.hton32(u32(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(int))
|
||||
formats << 1
|
||||
}
|
||||
i64 {
|
||||
types << u32(Oid.t_int8)
|
||||
num := conv.hton64(u64(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(i64))
|
||||
formats << 1
|
||||
}
|
||||
f32 {
|
||||
types << u32(Oid.t_float4)
|
||||
num := conv.htonf32(f32(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(f32))
|
||||
formats << 1
|
||||
}
|
||||
f64 {
|
||||
types << u32(Oid.t_float8)
|
||||
num := conv.htonf64(f64(data))
|
||||
vals << &char(&num)
|
||||
lens << int(sizeof(f64))
|
||||
formats << 1
|
||||
}
|
||||
string {
|
||||
// If paramTypes is NULL, or any particular element in the array is zero,
|
||||
// the server infers a data type for the parameter symbol in the same way
|
||||
// it would do for an untyped literal string.
|
||||
types << u32(0)
|
||||
vals << &char(data.str)
|
||||
lens << data.len
|
||||
formats << 0
|
||||
}
|
||||
time.Time {
|
||||
datetime := data.format_ss()
|
||||
types << u32(0)
|
||||
vals << &char(datetime.str)
|
||||
lens << datetime.len
|
||||
formats << 0
|
||||
}
|
||||
orm.InfixType {
|
||||
pg_stmt_match(mut types, mut vals, mut lens, mut formats, data.right)
|
||||
}
|
||||
orm.Null {
|
||||
types << u32(0) // we do not know col type, let server infer
|
||||
vals << &char(unsafe { nil }) // NULL pointer indicates NULL
|
||||
lens << int(0) // ignored
|
||||
formats << 0 // ignored
|
||||
}
|
||||
[]orm.Primitive {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]bool {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]f32 {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]f64 {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]i16 {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]i64 {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]i8 {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]int {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]string {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]time.Time {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]u16 {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]u32 {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]u64 {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]u8 {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
[]orm.InfixType {
|
||||
pg_stmt_match_array(mut types, mut vals, mut lens, mut formats, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pg_type_from_v(typ int) !string {
|
||||
str := match typ {
|
||||
orm.type_idx['i8'], orm.type_idx['i16'], orm.type_idx['u8'], orm.type_idx['u16'] {
|
||||
'SMALLINT'
|
||||
}
|
||||
orm.type_idx['bool'] {
|
||||
'BOOLEAN'
|
||||
}
|
||||
orm.type_idx['int'], orm.type_idx['u32'] {
|
||||
'INT'
|
||||
}
|
||||
orm.time_ {
|
||||
'TIMESTAMP'
|
||||
}
|
||||
orm.enum_ {
|
||||
'BIGINT'
|
||||
}
|
||||
orm.type_idx['i64'], orm.type_idx['u64'] {
|
||||
'BIGINT'
|
||||
}
|
||||
orm.float[0] {
|
||||
'REAL'
|
||||
}
|
||||
orm.float[1] {
|
||||
'DOUBLE PRECISION'
|
||||
}
|
||||
orm.type_string {
|
||||
'TEXT'
|
||||
}
|
||||
orm.serial {
|
||||
'SERIAL'
|
||||
}
|
||||
else {
|
||||
''
|
||||
}
|
||||
}
|
||||
|
||||
if str == '' {
|
||||
return error('Unknown type ${typ}')
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
fn val_to_primitive(val ?string, typ int) !orm.Primitive {
|
||||
if str := val {
|
||||
match typ {
|
||||
// bool
|
||||
orm.type_idx['bool'] {
|
||||
return orm.Primitive(str == 't')
|
||||
}
|
||||
// i8
|
||||
orm.type_idx['i8'] {
|
||||
return orm.Primitive(str.i8())
|
||||
}
|
||||
// i16
|
||||
orm.type_idx['i16'] {
|
||||
return orm.Primitive(str.i16())
|
||||
}
|
||||
// int
|
||||
orm.type_idx['int'] {
|
||||
return orm.Primitive(str.int())
|
||||
}
|
||||
// i64
|
||||
orm.type_idx['i64'] {
|
||||
return orm.Primitive(str.i64())
|
||||
}
|
||||
// u8
|
||||
orm.type_idx['u8'] {
|
||||
data := str.i8()
|
||||
return orm.Primitive(*unsafe { &u8(&data) })
|
||||
}
|
||||
// u16
|
||||
orm.type_idx['u16'] {
|
||||
data := str.i16()
|
||||
return orm.Primitive(*unsafe { &u16(&data) })
|
||||
}
|
||||
// u32
|
||||
orm.type_idx['u32'] {
|
||||
data := str.int()
|
||||
return orm.Primitive(*unsafe { &u32(&data) })
|
||||
}
|
||||
// u64
|
||||
orm.type_idx['u64'] {
|
||||
data := str.i64()
|
||||
return orm.Primitive(*unsafe { &u64(&data) })
|
||||
}
|
||||
// f32
|
||||
orm.type_idx['f32'] {
|
||||
return orm.Primitive(str.f32())
|
||||
}
|
||||
// f64
|
||||
orm.type_idx['f64'] {
|
||||
return orm.Primitive(str.f64())
|
||||
}
|
||||
orm.type_string {
|
||||
return orm.Primitive(str)
|
||||
}
|
||||
orm.time_ {
|
||||
if str.contains_any(' /:-') {
|
||||
date_time_str := time.parse(str)!
|
||||
return orm.Primitive(date_time_str)
|
||||
}
|
||||
|
||||
timestamp := str.int()
|
||||
return orm.Primitive(time.unix(timestamp))
|
||||
}
|
||||
orm.enum_ {
|
||||
return orm.Primitive(str.i64())
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
return error('Unknown field type ${typ}')
|
||||
} else {
|
||||
return orm.Null{}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user