Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write to answer native columns and remove trigger #2755

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions backend/api/src/unresolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,15 +258,11 @@ const undoResolution = async (
}
if (contract.mechanism === 'cpmm-multi-1' && !answerId) {
// remove resolutionTime and resolverId from all answers in the contract
await pg.none(
`update answers
set data = data - 'resolutionTime' - 'resolverId'
where contract_id = $1`,
[contractId]
)
const newAnswers = await pg.map(
`update answers
set resolution_time = null
set
resolution_time = null,
resolver_id = null
where contract_id = $1
returning *`,
[contractId],
Expand All @@ -276,7 +272,11 @@ const undoResolution = async (
} else if (answerId) {
const answer = await pg.one(
`update answers
set data = data - '{resolution,resolutionTime,resolutionProbability,resolverId}'::text[]
set
resolution = null,
resolution_time = null,
resolution_probability = null,
resolver_id = null
where id = $1
returning *`,
[answerId],
Expand Down
8 changes: 4 additions & 4 deletions backend/shared/src/helpers/add-house-subsidy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ export const addHouseSubsidyToAnswer = async (

await tx.none(
`update answers
set data = data ||
jsonb_build_object('totalLiquidity', data->>'totalLiquidity'::numeric + $1) ||
jsonb_build_object('subsidyPool', data->>'subsidyPool'::numeric + $1)
set
total_liquidity = total_liquidity + $1,
subsidy_pool = subsidy_pool + $1
where id = $2`,
[amount, answerId]
)

await updateContract(pg, contractId, {
await updateContract(tx, contractId, {
totalLiquidity: FieldVal.increment(amount),
})
})
Expand Down
55 changes: 43 additions & 12 deletions backend/shared/src/supabase/answers.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { SupabaseDirectClient } from 'shared/supabase/init'
import { type SupabaseDirectClient } from 'shared/supabase/init'
import { convertAnswer } from 'common/supabase/contracts'
import { groupBy } from 'lodash'
import { Answer } from 'common/answer'
import {
bulkInsert,
updateData,
insert,
DataUpdate,
bulkUpdateData,
bulkUpdate,
update,
} from './utils'
import { randomString } from 'common/util/random'
import { removeUndefinedProps } from 'common/util/object'
import { DataFor, millisToTs } from 'common/supabase/utils'
import { Row, millisToTs } from 'common/supabase/utils'
import {
broadcastNewAnswer,
broadcastUpdatedAnswers,
} from 'shared/websockets/helpers'
import { pick } from 'lodash'

export const getAnswer = async (pg: SupabaseDirectClient, id: string) => {
const row = await pg.oneOrNone(`select * from answers where id = $1`, [id])
Expand Down Expand Up @@ -72,9 +73,14 @@ export const bulkInsertAnswers = async (
export const updateAnswer = async (
pg: SupabaseDirectClient,
answerId: string,
update: DataUpdate<'answers'>
data: Partial<Answer>
) => {
const row = await updateData(pg, 'answers', 'id', { ...update, id: answerId })
const row = await update(
pg,
'answers',
'id',
partialAnswerToRow({ ...data, id: answerId })
)
const answer = convertAnswer(row)
broadcastUpdatedAnswers(answer.contractId, [answer])
return answer
Expand All @@ -83,21 +89,46 @@ export const updateAnswer = async (
export const updateAnswers = async (
pg: SupabaseDirectClient,
contractId: string,
updates: (Partial<DataFor<'answers'>> & { id: string })[]
updates: (Partial<Answer> & { id: string })[]
) => {
await bulkUpdateData<'answers'>(pg, 'answers', updates)
await bulkUpdate(pg, 'answers', ['id'], updates.map(partialAnswerToRow))

broadcastUpdatedAnswers(contractId, updates)
}

export const answerToRow = (answer: Omit<Answer, 'id'> & { id?: string }) => ({
id: 'id' in answer ? answer.id : randomString(),
const answerToRow = (answer: Omit<Answer, 'id'> & { id?: string }) => ({
id: answer.id,
index: answer.index,
contract_id: answer.contractId,
user_id: answer.userId,
text: answer.text,
color: answer.color,
pool_yes: answer.poolYes,
pool_no: answer.poolNo,
prob: answer.prob,
created_time: answer.createdTime ? millisToTs(answer.createdTime) : undefined,
data: JSON.stringify(removeUndefinedProps(answer)) + '::jsonb',
total_liquidity: answer.totalLiquidity,
subsidy_pool: answer.subsidyPool,
created_time: answer.createdTime
? millisToTs(answer.createdTime) + '::timestamptz'
: undefined,
resolution: answer.resolution,
resolution_time: answer.resolutionTime
? millisToTs(answer.resolutionTime) + '::timestamptz'
: undefined,
resolution_probability: answer.resolutionProbability,
resolver_id: answer.resolverId,
prob_change_day: answer.probChanges?.day,
prob_change_week: answer.probChanges?.week,
prob_change_month: answer.probChanges?.month,
data:
JSON.stringify(
removeUndefinedProps(pick(answer, ['isOther', 'loverUserId']))
) + '::jsonb',
})

// does not convert isOther, loverUserId
const partialAnswerToRow = (answer: Partial<Answer>) => {
const partial: any = removeUndefinedProps(answerToRow(answer as any))
delete partial.data
return partial as Partial<Row<'answers'>>
}
56 changes: 42 additions & 14 deletions backend/shared/src/supabase/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { sortBy } from 'lodash'
import { pgp, SupabaseDirectClient, SupabaseDirectClientTimeout } from './init'
import { pgp, SupabaseDirectClient } from './init'
import { DataFor, Tables, TableName, Column, Row } from 'common/supabase/utils'

export async function getIds<T extends TableName>(
Expand All @@ -16,8 +16,8 @@ export async function insert<
const columnNames = Object.keys(values)
const cs = new pgp.helpers.ColumnSet(columnNames, { table })
const query = pgp.helpers.insert(values, cs)
// Hack to properly cast jsonb values.
const q = query.replace(/::jsonb'/g, "'::jsonb")
// Hack to properly cast values.
const q = query.replace(/::(\w*)'/g, "'::$1")
return await db.one<Row<T>>(q + ` returning *`)
}

Expand All @@ -31,30 +31,58 @@ export async function bulkInsert<
const columnNames = Object.keys(values[0])
const cs = new pgp.helpers.ColumnSet(columnNames, { table })
const query = pgp.helpers.insert(values, cs)
// Hack to properly cast jsonb values.
const q = query.replace(/::jsonb'/g, "'::jsonb")
// Hack to properly cast values.
const q = query.replace(/::(\w*)'/g, "'::$1")
return await db.many<Row<T>>(q + ` returning *`)
}

export async function update<
T extends TableName,
ColumnValues extends Tables[T]['Update']
>(
db: SupabaseDirectClient,
table: T,
idField: Column<T>,
values: ColumnValues
) {
const columnNames = Object.keys(values)
const cs = new pgp.helpers.ColumnSet(columnNames, { table })
if (!(idField in values)) {
throw new Error(`missing ${idField} in values for ${columnNames}`)
}
const clause = pgp.as.format(
`${idField} = $1`,
values[idField as keyof ColumnValues]
)
const query = pgp.helpers.update(values, cs) + ` WHERE ${clause}`
// Hack to properly cast values.
const q = query.replace(/::(\w*)'/g, "'::$1")
return await db.one<Row<T>>(q + ` returning *`)
}

export async function bulkUpdate<
T extends TableName,
ColumnValues extends Tables[T]['Update'],
Row extends Tables[T]['Row']
ColumnValues extends Tables[T]['Update']
>(
db: SupabaseDirectClientTimeout,
db: SupabaseDirectClient,
table: T,
idFields: (string & keyof Row)[],
idFields: Column<T>[],
values: ColumnValues[],
timeoutMs?: number
timeoutMs?: number // only works with SupabaseDirectClientTimeout
) {
if (values.length) {
const columnNames = Object.keys(values[0])
const cs = new pgp.helpers.ColumnSet(columnNames, { table })
const clause = idFields.map((f) => `v.${f} = t.${f}`).join(' and ')
const query = pgp.helpers.update(values, cs) + ` WHERE ${clause}`
// Hack to properly cast jsonb values.
const q = query.replace(/::jsonb'/g, "'::jsonb")
// Hack to properly cast values.
const q = query.replace(/::(\w*)'/g, "'::$1")
if (timeoutMs) {
if (!('timeout' in db)) {
throw new Error(
'bulkUpdate with timeoutMs is not supported in a transaction'
)
}
await db.timeout(timeoutMs, (t) => t.none(q))
} else {
await db.none(q)
Expand All @@ -78,8 +106,8 @@ export async function bulkUpsert<
const columnNames = Object.keys(values[0])
const cs = new pgp.helpers.ColumnSet(columnNames, { table })
const baseQuery = pgp.helpers.insert(values, cs)
// Hack to properly cast jsonb values.
const baseQueryReplaced = baseQuery.replace(/::jsonb'/g, "'::jsonb")
// Hack to properly cast values.
const baseQueryReplaced = baseQuery.replace(/::(\w*)'/g, "'::$1")

const primaryKey = Array.isArray(idField) ? idField.join(', ') : idField
const upsertAssigns = cs.assignColumns({ from: 'excluded', skip: idField })
Expand Down
36 changes: 0 additions & 36 deletions backend/supabase/answers.sql
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,6 @@ create table if not exists
resolver_id text
);

-- Triggers
create trigger answers_populate before insert
or
update on public.answers for each row
execute function answers_populate_cols ();

-- Functions
create
or replace function public.answers_populate_cols () returns trigger language plpgsql as $function$
begin
if new.data is not null then
new.index := ((new.data) -> 'index')::int;
new.contract_id := (new.data) ->> 'contractId';
new.user_id := (new.data) ->> 'userId';
new.text := (new.data) ->> 'text';
new.created_time :=
case when new.data ? 'createdTime' then millis_to_ts(((new.data) ->> 'createdTime')::bigint) else null end;
new.color := (new.data) ->> 'color';
new.pool_yes := ((new.data) ->> 'poolYes')::numeric;
new.pool_no := ((new.data) ->> 'poolNo')::numeric;
new.prob := ((new.data) ->> 'prob')::numeric;
new.total_liquidity := ((new.data) ->> 'totalLiquidity')::numeric;
new.subsidy_pool := ((new.data) ->> 'subsidyPool')::numeric;
new.prob_change_day := ((new.data) -> 'probChanges'->>'day')::numeric;
new.prob_change_week := ((new.data) -> 'probChanges'->>'week')::numeric;
new.prob_change_month := ((new.data) -> 'probChanges'->>'month')::numeric;
new.resolution_time :=
case when new.data ? 'resolutionTime' then millis_to_ts(((new.data) ->> 'resolutionTime')::bigint) end;
new.resolution_probability := ((new.data) -> 'resolutionProbability')::numeric;
new.resolution := (new.data) ->> 'resolution';
new.resolver_id := (new.data) ->> 'resolverId';
end if;
return new;
end
$function$;

-- Policies
alter table answers enable row level security;

Expand Down
Loading