// PrepparedQuery is used when restoring from a snapshot. For general inserts, // use PreparedQuerySet. func (s *StateRestore) PreparedQuery(query *structs.PreparedQuery) error { // If this is a template, compile it, otherwise leave the compiled // template field nil. var ct *prepared_query.CompiledTemplate if prepared_query.IsTemplate(query) { var err error ct, err = prepared_query.Compile(query) if err != nil { return fmt.Errorf("failed compiling template: %s", err) } } // Insert the wrapped query. if err := s.tx.Insert("prepared-queries", &queryWrapper{query, ct}); err != nil { return fmt.Errorf("failed restoring prepared query: %s", err) } if err := indexUpdateMaxTxn(s.tx, query.ModifyIndex, "prepared-queries"); err != nil { return fmt.Errorf("failed updating index: %s", err) } s.watches.Arm("prepared-queries") return nil }
// preparedQuerySetTxn is the inner method used to insert a prepared query with // the proper indexes into the state store. func (s *StateStore) preparedQuerySetTxn(tx *memdb.Txn, idx uint64, query *structs.PreparedQuery) error { // Check that the ID is set. if query.ID == "" { return ErrMissingQueryID } // Check for an existing query. wrapped, err := tx.First("prepared-queries", "id", query.ID) if err != nil { return fmt.Errorf("failed prepared query lookup: %s", err) } existing := toPreparedQuery(wrapped) // Set the indexes. if existing != nil { query.CreateIndex = existing.CreateIndex query.ModifyIndex = idx } else { query.CreateIndex = idx query.ModifyIndex = idx } // Verify that the query name doesn't already exist, or that we are // updating the same instance that has this name. If this is a template // and the name is empty then we make sure there's not an empty template // already registered. if query.Name != "" { wrapped, err := tx.First("prepared-queries", "name", query.Name) if err != nil { return fmt.Errorf("failed prepared query lookup: %s", err) } other := toPreparedQuery(wrapped) if other != nil && (existing == nil || existing.ID != other.ID) { return fmt.Errorf("name '%s' aliases an existing query name", query.Name) } } else if prepared_query.IsTemplate(query) { wrapped, err := tx.First("prepared-queries", "template", query.Name) if err != nil { return fmt.Errorf("failed prepared query lookup: %s", err) } other := toPreparedQuery(wrapped) if other != nil && (existing == nil || existing.ID != other.ID) { return fmt.Errorf("a query template with an empty name already exists") } } // Verify that the name doesn't alias any existing ID. We allow queries // to be looked up by ID *or* name so we don't want anyone to try to // register a query with a name equal to some other query's ID in an // attempt to hijack it. We also look up by ID *then* name in order to // prevent this, but it seems prudent to prevent these types of rogue // queries from ever making it into the state store. Note that we have // to see if the name looks like a UUID before checking since the UUID // index will complain if we look up something that's not formatted // like one. if isUUID(query.Name) { wrapped, err := tx.First("prepared-queries", "id", query.Name) if err != nil { return fmt.Errorf("failed prepared query lookup: %s", err) } if wrapped != nil { return fmt.Errorf("name '%s' aliases an existing query ID", query.Name) } } // Verify that the session exists. if query.Session != "" { sess, err := tx.First("sessions", "id", query.Session) if err != nil { return fmt.Errorf("failed session lookup: %s", err) } if sess == nil { return fmt.Errorf("invalid session %#v", query.Session) } } // We do not verify the service here, nor the token, if any. These are // checked at execute time and not doing integrity checking on them // helps avoid bootstrapping chicken and egg problems. // If this is a template, compile it, otherwise leave the compiled // template field nil. var ct *prepared_query.CompiledTemplate if prepared_query.IsTemplate(query) { var err error ct, err = prepared_query.Compile(query) if err != nil { return fmt.Errorf("failed compiling template: %s", err) } } // Insert the wrapped query. if err := tx.Insert("prepared-queries", &queryWrapper{query, ct}); err != nil { return fmt.Errorf("failed inserting prepared query: %s", err) } if err := tx.Insert("index", &IndexEntry{"prepared-queries", idx}); err != nil { return fmt.Errorf("failed updating index: %s", err) } tx.Defer(func() { s.tableWatches["prepared-queries"].Notify() }) return nil }