Recovered source transcript
1// Code generated by sqlc. DO NOT EDIT.
2// versions:
3// sqlc v1.31.1
4// source: candidates.sql
5
6package dbgen
7
8import (
9 "context"
10 "time"
11
12 "github.com/google/uuid"
13 "github.com/jackc/pgx/v5/pgtype"
14 pgvector "github.com/pgvector/pgvector-go"
15)
16
17const addCandidateCertification = `-- name: AddCandidateCertification :exec
18INSERT INTO candidate_certifications (organization_id, candidate_id, name)
19VALUES ($1,$2,$3)
20ON CONFLICT (candidate_id, name) DO NOTHING
21`
22
23type AddCandidateCertificationParams struct {
24 OrganizationID uuid.UUID `json:"organization_id"`
25 CandidateID uuid.UUID `json:"candidate_id"`
26 Name string `json:"name"`
27}
28
29func (q *Queries) AddCandidateCertification(ctx context.Context, arg AddCandidateCertificationParams) error {
30 _, err := q.db.Exec(ctx, addCandidateCertification, arg.OrganizationID, arg.CandidateID, arg.Name)
31 return err
32}
33
34const addCandidateCustomerExperience = `-- name: AddCandidateCustomerExperience :exec
35INSERT INTO candidate_customer_experience (organization_id, candidate_id, agency)
36VALUES ($1,$2,$3)
37ON CONFLICT (candidate_id, agency) DO NOTHING
38`
39
40type AddCandidateCustomerExperienceParams struct {
41 OrganizationID uuid.UUID `json:"organization_id"`
42 CandidateID uuid.UUID `json:"candidate_id"`
43 Agency string `json:"agency"`
44}
45
46func (q *Queries) AddCandidateCustomerExperience(ctx context.Context, arg AddCandidateCustomerExperienceParams) error {
47 _, err := q.db.Exec(ctx, addCandidateCustomerExperience, arg.OrganizationID, arg.CandidateID, arg.Agency)
48 return err
49}
50
51const addCandidateSkill = `-- name: AddCandidateSkill :exec
52INSERT INTO candidate_skills (organization_id, candidate_id, skill_id)
53VALUES ($1,$2,$3)
54ON CONFLICT (candidate_id, skill_id) DO NOTHING
55`
56
57type AddCandidateSkillParams struct {
58 OrganizationID uuid.UUID `json:"organization_id"`
59 CandidateID uuid.UUID `json:"candidate_id"`
60 SkillID uuid.UUID `json:"skill_id"`
61}
62
63func (q *Queries) AddCandidateSkill(ctx context.Context, arg AddCandidateSkillParams) error {
64 _, err := q.db.Exec(ctx, addCandidateSkill, arg.OrganizationID, arg.CandidateID, arg.SkillID)
65 return err
66}
67
68const anonymizeCandidate = `-- name: AnonymizeCandidate :execrows
69UPDATE candidates SET
70 full_name = '[erased]',
71 email = NULL,
72 headline = NULL,
73 location = NULL,
74 resume_embedding = NULL,
75 external_id = NULL,
76 available = false,
77 anonymized_at = now(),
78 updated_at = now()
79WHERE id = $1 AND anonymized_at IS NULL
80`
81
82// Right to erasure as anonymize in place: wipe the identifying PII while KEEPING the row so the de
83// identified scored outcomes (slate_members) and the demographic bucket survive for the adverse impact
84// record. Once the name, email, contact, and resume vector are gone the row is no longer personal data,
85// so erasure is satisfied and the compliance history is preserved. Idempotent on anonymized_at.
86func (q *Queries) AnonymizeCandidate(ctx context.Context, id uuid.UUID) (int64, error) {
87 result, err := q.db.Exec(ctx, anonymizeCandidate, id)
88 if err != nil {
89 return 0, err
90 }
91 return result.RowsAffected(), nil
92}
93
94const createCandidate = `-- name: CreateCandidate :one
95INSERT INTO candidates (
96 organization_id, full_name, email, headline,
97 clearance_level, clearance_poly, clearance_status,
98 years_experience, location, remote, relocatable, available, resume_embedding
99) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
100RETURNING id, organization_id, full_name, email, headline, clearance_level, clearance_poly, clearance_status, clearance_granted_at, clearance_expires_at, reinvestigation_due_at, years_experience, location, remote, relocatable, available, resume_embedding, search_tsv, created_at, updated_at, deleted_at, source, external_id, anonymized_at, clearance_provenance, clearance_verified_at
101`
102
103type CreateCandidateParams struct {
104 OrganizationID uuid.UUID `json:"organization_id"`
105 FullName string `json:"full_name"`
106 Email *string `json:"email"`
107 Headline *string `json:"headline"`
108 ClearanceLevel string `json:"clearance_level"`
109 ClearancePoly string `json:"clearance_poly"`
110 ClearanceStatus string `json:"clearance_status"`
111 YearsExperience int32 `json:"years_experience"`
112 Location *string `json:"location"`
113 Remote bool `json:"remote"`
114 Relocatable bool `json:"relocatable"`
115 Available bool `json:"available"`
116 ResumeEmbedding *pgvector.Vector `json:"resume_embedding"`
117}
118
119func (q *Queries) CreateCandidate(ctx context.Context, arg CreateCandidateParams) (Candidate, error) {
120 row := q.db.QueryRow(ctx, createCandidate,
121 arg.OrganizationID,
122 arg.FullName,
123 arg.Email,
124 arg.Headline,
125 arg.ClearanceLevel,
126 arg.ClearancePoly,
127 arg.ClearanceStatus,
128 arg.YearsExperience,
129 arg.Location,
130 arg.Remote,
131 arg.Relocatable,
132 arg.Available,
133 arg.ResumeEmbedding,
134 )
135 var i Candidate
136 err := row.Scan(
137 &i.ID,
138 &i.OrganizationID,
139 &i.FullName,
140 &i.Email,
141 &i.Headline,
142 &i.ClearanceLevel,
143 &i.ClearancePoly,
144 &i.ClearanceStatus,
145 &i.ClearanceGrantedAt,
146 &i.ClearanceExpiresAt,
147 &i.ReinvestigationDueAt,
148 &i.YearsExperience,
149 &i.Location,
150 &i.Remote,
151 &i.Relocatable,
152 &i.Available,
153 &i.ResumeEmbedding,
154 &i.SearchTsv,
155 &i.CreatedAt,
156 &i.UpdatedAt,
157 &i.DeletedAt,
158 &i.Source,
159 &i.ExternalID,
160 &i.AnonymizedAt,
161 &i.ClearanceProvenance,
162 &i.ClearanceVerifiedAt,
163 )
164 return i, err
165}
166
167const findCandidateByEmail = `-- name: FindCandidateByEmail :one
168SELECT id FROM candidates
169WHERE organization_id = $1 AND deleted_at IS NULL AND email IS NOT NULL AND lower(email) = lower($2)
170ORDER BY created_at LIMIT 1
171`
172
173type FindCandidateByEmailParams struct {
174 OrganizationID uuid.UUID `json:"organization_id"`
175 Lower string `json:"lower"`
176}
177
178// Identity resolution blocking key 1: an exact (case insensitive) email match in the same org. Email is
179// the strongest identity signal, so a sourced lead with a known email merges into the existing person.
180func (q *Queries) FindCandidateByEmail(ctx context.Context, arg FindCandidateByEmailParams) (uuid.UUID, error) {
181 row := q.db.QueryRow(ctx, findCandidateByEmail, arg.OrganizationID, arg.Lower)
182 var id uuid.UUID
183 err := row.Scan(&id)
184 return id, err
185}
186
187const findCandidateByNameLocation = `-- name: FindCandidateByNameLocation :one
188SELECT id FROM candidates
189WHERE organization_id = $1 AND deleted_at IS NULL
190 AND lower(full_name) = lower($2) AND lower(COALESCE(location, '')) = lower($3)
191ORDER BY created_at LIMIT 1
192`
193
194type FindCandidateByNameLocationParams struct {
195 OrganizationID uuid.UUID `json:"organization_id"`
196 Lower string `json:"lower"`
197 Lower_2 string `json:"lower_2"`
198}
199
200// Identity resolution blocking key 2: same normalized full name AND same normalized location in the org.
201// Name alone is too weak (two John Smiths); name plus location is a defensible second signal. The caller
202// only invokes this with a non empty location.
203func (q *Queries) FindCandidateByNameLocation(ctx context.Context, arg FindCandidateByNameLocationParams) (uuid.UUID, error) {
204 row := q.db.QueryRow(ctx, findCandidateByNameLocation, arg.OrganizationID, arg.Lower, arg.Lower_2)
205 var id uuid.UUID
206 err := row.Scan(&id)
207 return id, err
208}
209
210const getCandidate = `-- name: GetCandidate :one
211SELECT id, organization_id, full_name, email, headline, clearance_level, clearance_poly, clearance_status, clearance_granted_at, clearance_expires_at, reinvestigation_due_at, years_experience, location, remote, relocatable, available, resume_embedding, search_tsv, created_at, updated_at, deleted_at, source, external_id, anonymized_at, clearance_provenance, clearance_verified_at FROM candidates WHERE id = $1 AND deleted_at IS NULL
212`
213
214func (q *Queries) GetCandidate(ctx context.Context, id uuid.UUID) (Candidate, error) {
215 row := q.db.QueryRow(ctx, getCandidate, id)
216 var i Candidate
217 err := row.Scan(
218 &i.ID,
219 &i.OrganizationID,
220 &i.FullName,
221 &i.Email,
222 &i.Headline,
223 &i.ClearanceLevel,
224 &i.ClearancePoly,
225 &i.ClearanceStatus,
226 &i.ClearanceGrantedAt,
227 &i.ClearanceExpiresAt,
228 &i.ReinvestigationDueAt,
229 &i.YearsExperience,
230 &i.Location,
231 &i.Remote,
232 &i.Relocatable,
233 &i.Available,
234 &i.ResumeEmbedding,
235 &i.SearchTsv,
236 &i.CreatedAt,
237 &i.UpdatedAt,
238 &i.DeletedAt,
239 &i.Source,
240 &i.ExternalID,
241 &i.AnonymizedAt,
242 &i.ClearanceProvenance,
243 &i.ClearanceVerifiedAt,
244 )
245 return i, err
246}
247
248const linkSourcedCandidate = `-- name: LinkSourcedCandidate :exec
249UPDATE candidates SET
250 external_id = COALESCE(external_id, $2),
251 headline = COALESCE(headline, $3),
252 years_experience = GREATEST(years_experience, $4),
253 updated_at = now()
254WHERE id = $1
255`
256
257type LinkSourcedCandidateParams struct {
258 ID uuid.UUID `json:"id"`
259 ExternalID *string `json:"external_id"`
260 Headline *string `json:"headline"`
261 YearsExperience int32 `json:"years_experience"`
262}
263
264// Identity resolution merge: link a sourced lead to an existing candidate. Sets the external id and
265// backfills headline and years ONLY where the existing row is empty or lower, never clobbering curated
266// bench data. Source is intentionally left unchanged so a verified bench person stays a bench person.
267func (q *Queries) LinkSourcedCandidate(ctx context.Context, arg LinkSourcedCandidateParams) error {
268 _, err := q.db.Exec(ctx, linkSourcedCandidate,
269 arg.ID,
270 arg.ExternalID,
271 arg.Headline,
272 arg.YearsExperience,
273 )
274 return err
275}
276
277const listCandidateCertifications = `-- name: ListCandidateCertifications :many
278SELECT name FROM candidate_certifications WHERE candidate_id = $1 ORDER BY name
279`
280
281func (q *Queries) ListCandidateCertifications(ctx context.Context, candidateID uuid.UUID) ([]string, error) {
282 rows, err := q.db.Query(ctx, listCandidateCertifications, candidateID)
283 if err != nil {
284 return nil, err
285 }
286 defer rows.Close()
287 items := []string{}
288 for rows.Next() {
289 var name string
290 if err := rows.Scan(&name); err != nil {
291 return nil, err
292 }
293 items = append(items, name)
294 }
295 if err := rows.Err(); err != nil {
296 return nil, err
297 }
298 return items, nil
299}
300
301const listCandidateCustomerExperience = `-- name: ListCandidateCustomerExperience :many
302SELECT agency FROM candidate_customer_experience WHERE candidate_id = $1 ORDER BY agency
303`
304
305func (q *Queries) ListCandidateCustomerExperience(ctx context.Context, candidateID uuid.UUID) ([]string, error) {
306 rows, err := q.db.Query(ctx, listCandidateCustomerExperience, candidateID)
307 if err != nil {
308 return nil, err
309 }
310 defer rows.Close()
311 items := []string{}
312 for rows.Next() {
313 var agency string
314 if err := rows.Scan(&agency); err != nil {
315 return nil, err
316 }
317 items = append(items, agency)
318 }
319 if err := rows.Err(); err != nil {
320 return nil, err
321 }
322 return items, nil
323}
324
325const listCandidateSkills = `-- name: ListCandidateSkills :many
326SELECT s.id, s.name
327FROM candidate_skills cs
328JOIN skills s ON s.id = cs.skill_id
329WHERE cs.candidate_id = $1
330ORDER BY s.name
331`
332
333type ListCandidateSkillsRow struct {
334 ID uuid.UUID `json:"id"`
335 Name string `json:"name"`
336}
337
338func (q *Queries) ListCandidateSkills(ctx context.Context, candidateID uuid.UUID) ([]ListCandidateSkillsRow, error) {
339 rows, err := q.db.Query(ctx, listCandidateSkills, candidateID)
340 if err != nil {
341 return nil, err
342 }
343 defer rows.Close()
344 items := []ListCandidateSkillsRow{}
345 for rows.Next() {
346 var i ListCandidateSkillsRow
347 if err := rows.Scan(&i.ID, &i.Name); err != nil {
348 return nil, err
349 }
350 items = append(items, i)
351 }
352 if err := rows.Err(); err != nil {
353 return nil, err
354 }
355 return items, nil
356}
357
358const listCandidates = `-- name: ListCandidates :many
359SELECT id, organization_id, full_name, email, headline, clearance_level, clearance_poly, clearance_status, clearance_granted_at, clearance_expires_at, reinvestigation_due_at, years_experience, location, remote, relocatable, available, resume_embedding, search_tsv, created_at, updated_at, deleted_at, source, external_id, anonymized_at, clearance_provenance, clearance_verified_at FROM candidates
360WHERE deleted_at IS NULL
361ORDER BY created_at DESC, id DESC
362LIMIT $1 OFFSET $2
363`
364
365type ListCandidatesParams struct {
366 Limit int32 `json:"limit"`
367 Offset int32 `json:"offset"`
368}
369
370// id is a unique trailing sort key so created_at ties give a total order and stable LIMIT/OFFSET paging.
371func (q *Queries) ListCandidates(ctx context.Context, arg ListCandidatesParams) ([]Candidate, error) {
372 rows, err := q.db.Query(ctx, listCandidates, arg.Limit, arg.Offset)
373 if err != nil {
374 return nil, err
375 }
376 defer rows.Close()
377 items := []Candidate{}
378 for rows.Next() {
379 var i Candidate
380 if err := rows.Scan(
381 &i.ID,
382 &i.OrganizationID,
383 &i.FullName,
384 &i.Email,
385 &i.Headline,
386 &i.ClearanceLevel,
387 &i.ClearancePoly,
388 &i.ClearanceStatus,
389 &i.ClearanceGrantedAt,
390 &i.ClearanceExpiresAt,
391 &i.ReinvestigationDueAt,
392 &i.YearsExperience,
393 &i.Location,
394 &i.Remote,
395 &i.Relocatable,
396 &i.Available,
397 &i.ResumeEmbedding,
398 &i.SearchTsv,
399 &i.CreatedAt,
400 &i.UpdatedAt,
401 &i.DeletedAt,
402 &i.Source,
403 &i.ExternalID,
404 &i.AnonymizedAt,
405 &i.ClearanceProvenance,
406 &i.ClearanceVerifiedAt,
407 ); err != nil {
408 return nil, err
409 }
410 items = append(items, i)
411 }
412 if err := rows.Err(); err != nil {
413 return nil, err
414 }
415 return items, nil
416}
417
418const listCandidatesForIntel = `-- name: ListCandidatesForIntel :many
419SELECT id, organization_id, full_name, email, headline, clearance_level, clearance_poly, clearance_status, clearance_granted_at, clearance_expires_at, reinvestigation_due_at, years_experience, location, remote, relocatable, available, resume_embedding, search_tsv, created_at, updated_at, deleted_at, source, external_id, anonymized_at, clearance_provenance, clearance_verified_at FROM candidates
420WHERE deleted_at IS NULL
421ORDER BY available DESC, updated_at DESC, created_at DESC, id
422LIMIT $1
423`
424
425// Available (staffable) candidates first, most recently updated next, so the workforce intelligence
426// surface leads with the talent you can deploy and newly staffed or sourced people are always scored.
427// The trailing id makes the order total, so the same set is selected before the LIMIT cut every run.
428func (q *Queries) ListCandidatesForIntel(ctx context.Context, limit int32) ([]Candidate, error) {
429 rows, err := q.db.Query(ctx, listCandidatesForIntel, limit)
430 if err != nil {
431 return nil, err
432 }
433 defer rows.Close()
434 items := []Candidate{}
435 for rows.Next() {
436 var i Candidate
437 if err := rows.Scan(
438 &i.ID,
439 &i.OrganizationID,
440 &i.FullName,
441 &i.Email,
442 &i.Headline,
443 &i.ClearanceLevel,
444 &i.ClearancePoly,
445 &i.ClearanceStatus,
446 &i.ClearanceGrantedAt,
447 &i.ClearanceExpiresAt,
448 &i.ReinvestigationDueAt,
449 &i.YearsExperience,
450 &i.Location,
451 &i.Remote,
452 &i.Relocatable,
453 &i.Available,
454 &i.ResumeEmbedding,
455 &i.SearchTsv,
456 &i.CreatedAt,
457 &i.UpdatedAt,
458 &i.DeletedAt,
459 &i.Source,
460 &i.ExternalID,
461 &i.AnonymizedAt,
462 &i.ClearanceProvenance,
463 &i.ClearanceVerifiedAt,
464 ); err != nil {
465 return nil, err
466 }
467 items = append(items, i)
468 }
469 if err := rows.Err(); err != nil {
470 return nil, err
471 }
472 return items, nil
473}
474
475const searchCandidatesHybrid = `-- name: SearchCandidatesHybrid :many
476WITH vec_raw AS MATERIALIZED (
477 SELECT id, (resume_embedding <=> $1::vector) AS distance
478 FROM candidates
479 WHERE deleted_at IS NULL AND resume_embedding IS NOT NULL
480 ORDER BY resume_embedding <=> $1::vector, id
481 LIMIT $2
482),
483vec AS (
484 SELECT id, ROW_NUMBER() OVER (ORDER BY distance, id) AS rnk
485 FROM vec_raw
486),
487fts AS (
488 SELECT id, ROW_NUMBER() OVER (
489 ORDER BY ts_rank(search_tsv, websearch_to_tsquery('english', $3)) DESC, id
490 ) AS rnk
491 FROM candidates
492 WHERE deleted_at IS NULL AND search_tsv @@ websearch_to_tsquery('english', $3)
493 ORDER BY ts_rank(search_tsv, websearch_to_tsquery('english', $3)) DESC, id
494 LIMIT $2
495)
496SELECT c.id, c.organization_id, c.full_name, c.email, c.headline, c.clearance_level, c.clearance_poly, c.clearance_status, c.clearance_granted_at, c.clearance_expires_at, c.reinvestigation_due_at, c.years_experience, c.location, c.remote, c.relocatable, c.available, c.resume_embedding, c.search_tsv, c.created_at, c.updated_at, c.deleted_at, c.source, c.external_id, c.anonymized_at, c.clearance_provenance, c.clearance_verified_at,
497 (COALESCE(1.0/(60+vec.rnk), 0) + COALESCE(1.0/(60+fts.rnk), 0))::float8 AS rrf_score
498FROM candidates c
499LEFT JOIN vec ON vec.id = c.id
500LEFT JOIN fts ON fts.id = c.id
501WHERE (vec.id IS NOT NULL OR fts.id IS NOT NULL) AND c.deleted_at IS NULL
502ORDER BY rrf_score DESC, c.created_at DESC, c.id
503LIMIT $2
504`
505
506type SearchCandidatesHybridParams struct {
507 Column1 pgvector.Vector `json:"column_1"`
508 Limit int32 `json:"limit"`
509 WebsearchToTsquery string `json:"websearch_to_tsquery"`
510}
511
512type SearchCandidatesHybridRow struct {
513 ID uuid.UUID `json:"id"`
514 OrganizationID uuid.UUID `json:"organization_id"`
515 FullName string `json:"full_name"`
516 Email *string `json:"email"`
517 Headline *string `json:"headline"`
518 ClearanceLevel string `json:"clearance_level"`
519 ClearancePoly string `json:"clearance_poly"`
520 ClearanceStatus string `json:"clearance_status"`
521 ClearanceGrantedAt pgtype.Date `json:"clearance_granted_at"`
522 ClearanceExpiresAt pgtype.Date `json:"clearance_expires_at"`
523 ReinvestigationDueAt pgtype.Date `json:"reinvestigation_due_at"`
524 YearsExperience int32 `json:"years_experience"`
525 Location *string `json:"location"`
526 Remote bool `json:"remote"`
527 Relocatable bool `json:"relocatable"`
528 Available bool `json:"available"`
529 ResumeEmbedding *pgvector.Vector `json:"resume_embedding"`
530 SearchTsv interface{} `json:"search_tsv"`
531 CreatedAt time.Time `json:"created_at"`
532 UpdatedAt time.Time `json:"updated_at"`
533 DeletedAt *time.Time `json:"deleted_at"`
534 Source string `json:"source"`
535 ExternalID *string `json:"external_id"`
536 AnonymizedAt *time.Time `json:"anonymized_at"`
537 ClearanceProvenance string `json:"clearance_provenance"`
538 ClearanceVerifiedAt *time.Time `json:"clearance_verified_at"`
539 RrfScore float64 `json:"rrf_score"`
540}
541
542// Hybrid retrieval: pgvector cosine kNN fused with full-text rank via Reciprocal Rank Fusion (k=60).
543// RLS already scopes both legs to the caller's org. $1=query embedding, $2=limit, $3=text query.
544// The vector arm is MATERIALIZED so the relaxed_order HNSW output is re-sorted by TRUE cosine distance
545// before it is ranked: under hnsw.iterative_scan = relaxed_order the index returns rows slightly out of
546// distance order, and ranking them without the re-sort would mis-rank the nearest neighbors at scale
547// (pgvector 0.8 README; AWS pgvector 0.8 Aurora guidance). Every ORDER BY carries a unique tiebreaker
548// (id) so results are byte-stable for fixed inputs, the AEDT reproducibility mandate. The caller SETs
549// LOCAL hnsw.ef_search and hnsw.iterative_scan first.
550func (q *Queries) SearchCandidatesHybrid(ctx context.Context, arg SearchCandidatesHybridParams) ([]SearchCandidatesHybridRow, error) {
551 rows, err := q.db.Query(ctx, searchCandidatesHybrid, arg.Column1, arg.Limit, arg.WebsearchToTsquery)
552 if err != nil {
553 return nil, err
554 }
555 defer rows.Close()
556 items := []SearchCandidatesHybridRow{}
557 for rows.Next() {
558 var i SearchCandidatesHybridRow
559 if err := rows.Scan(
560 &i.ID,
561 &i.OrganizationID,
562 &i.FullName,
563 &i.Email,
564 &i.Headline,
565 &i.ClearanceLevel,
566 &i.ClearancePoly,
567 &i.ClearanceStatus,
568 &i.ClearanceGrantedAt,
569 &i.ClearanceExpiresAt,
570 &i.ReinvestigationDueAt,
571 &i.YearsExperience,
572 &i.Location,
573 &i.Remote,
574 &i.Relocatable,
575 &i.Available,
576 &i.ResumeEmbedding,
577 &i.SearchTsv,
578 &i.CreatedAt,
579 &i.UpdatedAt,
580 &i.DeletedAt,
581 &i.Source,
582 &i.ExternalID,
583 &i.AnonymizedAt,
584 &i.ClearanceProvenance,
585 &i.ClearanceVerifiedAt,
586 &i.RrfScore,
587 ); err != nil {
588 return nil, err
589 }
590 items = append(items, i)
591 }
592 if err := rows.Err(); err != nil {
593 return nil, err
594 }
595 return items, nil
596}
597
598const setCandidateClearanceProvenance = `-- name: SetCandidateClearanceProvenance :execrows
599UPDATE candidates
600SET clearance_provenance = $2, clearance_verified_at = $3, updated_at = now()
601WHERE id = $1 AND deleted_at IS NULL
602`
603
604type SetCandidateClearanceProvenanceParams struct {
605 ID uuid.UUID `json:"id"`
606 ClearanceProvenance string `json:"clearance_provenance"`
607 ClearanceVerifiedAt *time.Time `json:"clearance_verified_at"`
608}
609
610// An org FSO records HOW a candidate's clearance is known. RLS scopes this to the caller's own org.
611// verified_at is the date the FSO confirmed it; it is NULL for a self reported indicator. The platform
612// never verifies a clearance itself, it records the org's authoritative assertion.
613func (q *Queries) SetCandidateClearanceProvenance(ctx context.Context, arg SetCandidateClearanceProvenanceParams) (int64, error) {
614 result, err := q.db.Exec(ctx, setCandidateClearanceProvenance, arg.ID, arg.ClearanceProvenance, arg.ClearanceVerifiedAt)
615 if err != nil {
616 return 0, err
617 }
618 return result.RowsAffected(), nil
619}
620
621const softDeleteCandidate = `-- name: SoftDeleteCandidate :exec
622UPDATE candidates SET deleted_at = now(), updated_at = now() WHERE id = $1
623`
624
625func (q *Queries) SoftDeleteCandidate(ctx context.Context, id uuid.UUID) error {
626 _, err := q.db.Exec(ctx, softDeleteCandidate, id)
627 return err
628}
629