Jump to content

php com base de dados - duvida sobre a inserção dos dados


Recommended Posts

Posted

Boas.

A minha dúvida tem a ver com o funcionamento integrade de PHP com SQL.

Resume-se ao seguinte:

Numa base de dados com um dos campos chamado "id" - com auto-increment - quando faço insert("qq coisa na base de dados") é criada uma nova "row" com o id a seguir ao último existente.

Como é que eu consigo obter uma variável $n igual ao "id" dessa nova row, numa situação em que a unica coluna defenida como "unique" é o próprio id?

portalmundial.net »» criamos sites

Posted

Boas.

A minha dúvida tem a ver com o funcionamento integrade de PHP com SQL.

Resume-se ao seguinte:

Numa base de dados com um dos campos chamado "id" - com auto-increment - quando faço insert("qq coisa na base de dados") é criada uma nova "row" com o id a seguir ao último existente.

Como é que eu consigo obter uma variável $n igual ao "id" dessa nova row, numa situação em que a unica coluna defenida como "unique" é o próprio id?

Esse comando não existe  " insert("qq coisa na base de dados")"  seria mysql_query("INSERT INTO tabela (id) VALUES('id')");

E para obter-se e mostrar-se os resultados podes fazer assim:

while($row = mysql_fetch_array("SELECT id FROM tabela")) {

echo $row['id'];
}
Posted

Tipicamente tens três formas:

1. Mantendo o id com autoincrement: após a inserção fazes um select à tabela para saber qual é o maior ID. Mas atenção que é necessário ter a tabela bloqueada durante essa operação de forma a não se obter o id errado. Isso deve ser feito de forma atómica através do uso de transacções, caso contrário não há qualquer garantia de que o id obtido seja o correcto.

2. Sem id com autoincrement:

2.1. Tens uma tabela de controlo em que guardas os último id gerado (ou o próximo id a usar), i.e. és tu quem faz a gestão do autoincrement. Mas atenção com a atomicidade das operações, problema idêntico ao do ponto 1. acima.

2.2. Procuras qual o maior id usado na tabela até à data, incrementas uma unidade e:

(a) enquanto o insert falhar, adicionar mais uma unidade e tentar novamente.

(b) deves colocar um limite de tentativas, por exemplo ao fim de 5 ou 10 tentativas assumes que não foi possível efectuar o insert e dás um erro.

PS: esta dúvida nada tem a ver com PHP mas sim com Bases de Dados, ou seja, está no tópico errado.

10 REM Generation 48K!
20 INPUT "URL:", A$
30 IF A$(1 TO 4) = "HTTP" THEN PRINT "400 Bad Request": GOTO 50
40 PRINT "404 Not Found"
50 PRINT "./M6 @ Portugal a Programar."

 

Posted

M6, acho que ele não queria saber isso tudo 😉

Muitas pessoas tentam mostrar que sabem fazer x e y  e quando querem ajudar um usuario em vez de ajudarem só complicam ainda mais o usuario.

Posted

M6, acho que ele não queria saber isso tudo 😉

Isso só ele poderá dizer. Eu achei que lhe devia mostrar quais as técnicas envolvidas para solucionar o problema em questão em vez de lhe dar uma única resposta.

Muitas pessoas tentam mostrar que sabem fazer x e y  e quando querem ajudar um usuario em vez de ajudarem só complicam ainda mais o usuario.

Usuário?!?!? ARG!!! :/

Não tem nada de complicado. O nuno_couto tem um problema, e eu apontei as 3 soluções tipicas para este problema, cabe ao nuno_couto saber qual a melhor solução para si e implementá-la.

Como referi acima, se desse apenas uma solução isso seria apenas limitar a passagem de conhecimento, assim quem tiver o mesmo problema e ler este tópico fica com um conhecimento muito melhor, muito superior ao que teria se eu simplesmente tivesse colocado aqui uma resposta. 😛

10 REM Generation 48K!
20 INPUT "URL:", A$
30 IF A$(1 TO 4) = "HTTP" THEN PRINT "400 Bad Request": GOTO 50
40 PRINT "404 Not Found"
50 PRINT "./M6 @ Portugal a Programar."

 

Posted

Agradeço a quem respondeu e de facto não considero que tenham complicado.

Mas a minha dúvida continua.

Vou explicar porquê.

Tipicamente tens três formas:

1. Mantendo o id com autoincrement: após a inserção fazes um select à tabela para saber qual é o maior ID. Mas atenção que é necessário ter a tabela bloqueada durante essa operação de forma a não se obter o id errado. Isso deve ser feito de forma atómica através do uso de transacções, caso contrário não há qualquer garantia de que o id obtido seja o correcto.

O problema é... Num "sistema" em que várias pessoas podem simultaneamente estar a introduzir dados na base de dados, o tempo entre a introdução dos dados e o query para ver o último ID, pode originar um ID errado. Daí a minha dúvida. Não existe um comando que "indique" qual a entrada que foi introduzida naquela operação?

Esse comando não existe  " insert("qq coisa na base de dados")"  seria mysql_query("INSERT INTO tabela (id) VALUES('id')");

kingless... o termo que usei foi mesmo só pra despachar... eu já faço programação à alguns meses e estou minimamento habituado aos termos...

Num próximo post serei mais técnico.

Obrigado pela ajuda.

Se alguem souber dar uma dica em relação à questão que coloquei, agradeço.

portalmundial.net »» criamos sites

Posted
Em 24/06/2006 às 17:42, kingless disse:

Quando das 3 opções a uma pessoa que não sabe nem fazer um insert na mysql é só para complicar ele!!

Essa é a tua opinião, a qual eu e o nuno_couto não partilhamos.

O nuno_couto sabia fazer um insert, só não sabia como obter o id, e foi nesse contexto que eu expliquei 3 técnicas possíveis.

10 REM Generation 48K!
20 INPUT "URL:", A$
30 IF A$(1 TO 4) = "HTTP" THEN PRINT "400 Bad Request": GOTO 50
40 PRINT "404 Not Found"
50 PRINT "./M6 @ Portugal a Programar."

 

Posted

Apesar da linguagem que utilizei não ter sido técnica, penso que a minha dúvida era suficiente para perceber que eu saberia fazer um insert na base de dados.

Se assim não fosse, não chegava a ter a dúvida que coloquei neste post.

Numa base de dados com um dos campos chamado "id" - com auto-increment - quando faço insert("qq coisa na base de dados") é criada uma nova "row" com o id a seguir ao último existente.

Como é que eu consigo obter uma variável $n igual ao "id" dessa nova row, numa situação em que a unica coluna defenida como "unique" é o próprio id?

Apesar da linguagem... não me parece que tenha demonstrado tanta falta de conhecimento... Se não soubesse usar um comando do tipo

mysql_query("INSERT INTO dados (nome, morada) VALUES ('$nome', '$morada')" , $bd);

como poderia saber que se ia criar uma nova row no campo "id" com auto-increment? ...

Penso que comentários desses são desnecessários num fórum em que é normal que os utilizadores mais experientes ajudem os novatos...

portalmundial.net »» criamos sites

Posted

Agradeço a quem respondeu e de facto não considero que tenham complicado.

Mas a minha dúvida continua.

Vou explicar porquê.

Tipicamente tens três formas:

1. Mantendo o id com autoincrement: após a inserção fazes um select à tabela para saber qual é o maior ID. Mas atenção que é necessário ter a tabela bloqueada durante essa operação de forma a não se obter o id errado. Isso deve ser feito de forma atómica através do uso de transacções, caso contrário não há qualquer garantia de que o id obtido seja o correcto.

O problema é... Num "sistema" em que várias pessoas podem simultaneamente estar a introduzir dados na base de dados, o tempo entre a introdução dos dados e o query para ver o último ID, pode originar um ID errado. Daí a minha dúvida. Não existe um comando que "indique" qual a entrada que foi introduzida naquela operação?

Ao que parece o PHP/MySQL quem uma coisa desse tipo, desconhecia e também eu aprendi com a tua dúvida. 😉

Como expliquei, neste tipo de situações usa-se controlo transaccional de forma a garantir que não há geração/obtenção de ids errados. Se procurares por "transaction commit rollback" no Google vais encontrar imensa informação.

Esse comando não existe  " insert("qq coisa na base de dados")"  seria mysql_query("INSERT INTO tabela (id) VALUES('id')");

kingless... o termo que usei foi mesmo só pra despachar... eu já faço programação à alguns meses e estou minimamento habituado aos termos...

Num próximo post serei mais técnico.

Obrigado pela ajuda.

Se alguem souber dar uma dica em relação à questão que coloquei, agradeço.

Ao que parece o comando existia mesmo, como disse acima, também eu aumentei os meus conhecimentos. 😁

10 REM Generation 48K!
20 INPUT "URL:", A$
30 IF A$(1 TO 4) = "HTTP" THEN PRINT "400 Bad Request": GOTO 50
40 PRINT "404 Not Found"
50 PRINT "./M6 @ Portugal a Programar."

 

Posted

Pessoal... tão mas que raio. Pa... já lhe dei a resposta usando apenas duas linhas, e era o que ele queria.

O que quer dizer que ele foi bem explicito. Ele tem razão, lendo a questão dele percebe-se bem o que quer. Tudo o que ele precisava era saber da existencia da função mysql_insert_id, que de resto já lhe apontei.

M6, essas formas que disseste não são boa prática pois causam lentidão, aumentam a complexidade e pior de tudo são falíveis.

Isso faz-se com a função mysql_insert_id() que é para isso que ela serve. Estar a aumentar a complexidade do modelo de dados só por motivos programáticos vai contra o principio de utilização de um sistema de gestão de base de dados, pois é precisamente para o contrário disso que ele existe.

Depois há outra coisa... o mysql até à sua versão 5 NÃO é sum SGBD transacional, ou seja, não suporta transações.

Kingless, demasiado off-topic e um pouco de flamming. Centra-te no assunto da thread.

Posted

Pessoal... tão mas que raio. Pa... já lhe dei a resposta usando apenas duas linhas, e era o que ele queria.

O que quer dizer que ele foi bem explicito. Ele tem razão, lendo a questão dele percebe-se bem o que quer. Tudo o que ele precisava era saber da existencia da função mysql_insert_id, que de resto já lhe apontei.

M6, essas formas que disseste não são boa prática pois causam lentidão, aumentam a complexidade e pior de tudo são falíveis.

Errado meu caro.

As soluções que apresentei são usadas diariamente em inumeros sistemas por esse mundo fora. São lentas ao ponto do SGBD estar bem, ou mal, configurado e se é um facto que têm alguma complexidade (se bem que bastante baixa ao nível dos SGBDs) não são em nada faliveis como afirmas, o que eu expliquei foi exactamente algumas das formas correctas e fiáveis de fazer a coisa.

Isso faz-se com a função mysql_insert_id() que é para isso que ela serve. Estar a aumentar a complexidade do modelo de dados só por motivos programáticos vai contra o principio de utilização de um sistema de gestão de base de dados, pois é precisamente para o contrário disso que ele existe.

Não conheço a implementação do mysql_insert_id mas aposto que por baixo usa uma destas técnicas, ou semelhante.

O modelo de dados não ia aumentar de complexidade, o mesmo nem necessita alteração para se usar algumas das técnicas, nem tal seria por razões programáticas como afirmas mas sim para colmatar uma lacuna no SGBD.

Depois há outra coisa... o mysql até à sua versão 5 NÃO é sum SGBD transacional, ou seja, não suporta transações.

Isso é um problema de quem usa o MySQL antes da versão 5. Por omissão da versão assumi a última.

Kingless, demasiado off-topic e um pouco de flamming. Centra-te no assunto da thread.

10 REM Generation 48K!
20 INPUT "URL:", A$
30 IF A$(1 TO 4) = "HTTP" THEN PRINT "400 Bad Request": GOTO 50
40 PRINT "404 Not Found"
50 PRINT "./M6 @ Portugal a Programar."

 

Posted

é um problema de quem usa o mysql antes da versão 5?

epa... há incompatibilidades... isto n é actualizar e pronto... quem tenha uma apliação a corrrer mysql4 pode ter muuuuuuuuito trabalho para a a migrar para o cinco.

Foi por isso que disse que eram faliveis pois não suportam trasações.

Epa... o mysql_insert_id() usa o identificador da ligação que contem informação sobre a utilização da mesma... não vejo qual o sentido de não o usar, seria só complicar o código e dar lugar a eventuais bugs e problemas de compatibilidade.

Os sistemas de gestão de bases de dados estão sempre a evoluir, este tipo de funcionalidade já é suportada por quase todas as APIs para quase todas as linguagens para quase todos os SGBDs... pa não vejo a razão para não as usar.

Posted

é um problema de quem usa o mysql antes da versão 5?

Sim, é.

No limite, se era necessário ter controlo transaccional e o SGBD escolhido não possui essa característa é um erro grosseiro de quem escolheu/desenvolveu a componente técnica para a arquitectura em causa.

epa... há incompatibilidades... isto n é actualizar e pronto... quem tenha uma apliação a corrrer mysql4 pode ter muuuuuuuuito trabalho para a a migrar para o cinco.

Foi por isso que disse que eram faliveis pois não suportam trasações.

Não tem a ver com actualizações mas sim com escolha: como referi acima, se se necessita de uma funcionalidade e o SGBD (ou qualquer outra peça da infraestutura) escolhido não suporta isso, então a escolha foi totalmente errada.

Epa... o mysql_insert_id() usa o identificador da ligação que contem informação sobre a utilização da mesma... não vejo qual o sentido de não o usar, seria só complicar o código e dar lugar a eventuais bugs e problemas de compatibilidade.

Estando essa função disponível faz todo o sentido usá-la. No entanto, o uso de controlo transaccional não complica, antes pelo contrário, simplifica a vida de quem trabalha com SGBDs além de que, não sei onde foste buscar essa ideia, não "dá lugar a bugs" - é como qualquer outra linguagem - e muito menos problemas de compatibilidade. Desenvolves assumidamente para uma determinada plataforma/sistema/versão e isso não possui qualquer problema de compatibilidade.

Os sistemas de gestão de bases de dados estão sempre a evoluir, este tipo de funcionalidade já é suportada por quase todas as APIs para quase todas as linguagens para quase todos os SGBDs... pa não vejo a razão para não as usar.

Por acaso há uma razão para a não usar e optar pelo controlo transaccional: quando possuis um conjunto de operações que têm de ser feitas de forma atómica não se deve optar pelo uso dessa função. Tal deve-se a dois factores muito simples: performance, a execução de um conjunto de comandos do lado do SGBD é muito superior à execução de um comando de cada vez por invocação do PHP (ou de qualquer outra linguagem) e pelo facto de que tal permite uma lógica muito mais simples, limpa e correcta em vez de se misturar tudo entre PHP e MySQL.

Vou clarificar com um exemplo. Imaginem que têm um formulário em PHP para criação de utilizadores com a seguinte estrutura:

Utilizador (utilizador_id: autoincrement, nome, password)

Morada (morada_id: autoincrement, fk_utilizador_id, morada, codigo_postal)

Os campos utilizador_id e morada_id são auto incrementados e o campo fk_utilizador_id é uma chave estrangeira para o campo utilizador_id.

A regra de criação de utilizadores diz que:

1. A criação de um utilizador necessita obrigatoriamente dos dados: nome, password, morada e código postal.

2. Um utilizador só pode ser criado com o total preenchimento da estrutura adjacente aos dados em causa.

A forma mas correcta de fazer isto é com controlo transaccional, de forma simples:

begin transaction;

lock utilizador for reading

insert into utilizador (nome, morada) values ('[nome]' , '[morada]');

if error = 0 then

  select max(utilizador_id) into USER_ID from utilizador;

  insert into morada (fk_user_id, morada, codigo_postal) values (USER_ID, '[morada]', '[codigo postal]');

end if

if error = 0 then

  commit;

else

  rollback;

end if

end transaction;

É óbvio que também se pode executar estes comandos através de programação PHP, mas não é a forma correcta, caso a inserção do utilizador seja ok mas a da morada falhe, vai ser necessário "materlar" a BD para corrigir o estado de inconsistência em que a mesma ficou, além de que se misturam conceitos e lógicas (de apresentação, de acesso aos dados, de negócio).

10 REM Generation 48K!
20 INPUT "URL:", A$
30 IF A$(1 TO 4) = "HTTP" THEN PRINT "400 Bad Request": GOTO 50
40 PRINT "404 Not Found"
50 PRINT "./M6 @ Portugal a Programar."

 

Posted

Oh m6... n estás a anailsar bem a coisa... a função mysql_insert_id vai buscar o id ao ultimo resultset... logo o que dissete logo após a ultima citação está completamente errado.

Não só escusas de fazer mais pedidos à base de dados como não usas mais instruções... usas brutalmente menos, e a lógica é muito mais simples. N sei como raio é que dizes que é uma logica mais complicada  e que se mistura mais alhos com bugalhos... para usares o valor do ultimo id inserido sem recurso a esta função terias que misturar muitas mais coisas... pegar valores da base de dados e usar o php para compara-los etc etc.

A questão da escolha não se poe neste caso. foi um membro que colocou uma questão... não lhe vamos dizer: "usa outro sgbd" principalmente qdo isto é uma coisa usada amplamente por milhares de programadores... na verdade mais pessoas devem usar a fução mysl_insert_id do que o oracle para fazer o que quer que seja, por exemplo.

O ultimo paragrafo, para alem de estar incorrecto ( já vou explicar porque ) não se aplica neste caso.

fazes um query usando  a função mysql_query()... se for  bem sucedido gera-se um resulset, seguidamente pegas nele e extrais o id inserido... se a inserção tiver falhado o php nem sequer te vai compilar o próximo pedido, pelo que não martelas nada para a base de dados em situação alguma.

Isto é o mais simples possivel... e não acho que é misturar logica nenhuma pois simplesmente dás instruções por fora e não te preocupas com a base de dados que ela fa o trablho dela.

O que disseste não se aplica neste caso pois a o segundo query só é execurtado se o primeiro for bem sucedido, o que garante conformidade dos dados em qq situação.

Pa... eu n quero ser o teimoso cá do sitio, estou a dizer isto porque acho que complicaste demasiado uma coisa simples... n é puxar a brasa a minha sardinha nem nada, por isso é que estou a fundamentar. Qdo clicar em "enviar" o SMF lá vai fazer mais uma vez o que estou a dizer.. e não usa o método que disseste pela simples razão que o php já trata disso de forma eficiente do que qq código escrito em php por parte do programador, visto a função tar implementtada em mais baixo nivel.

Pa... eu sei o que estou a dizer e n é para te chatear ou contrariar nem nada disso... de resto podes fazer o teste... cria duas bds clones usa massivamente o teu método e o meu para fazer insersoes... faz um script que soma todos os tempos de execução e numero de queries e vê por ti próprio o preço de não simplificar as coisas.

Posted

Oh m6... n estás a anailsar bem a coisa... a função mysql_insert_id vai buscar o id ao ultimo resultset... logo o que dissete logo após a ultima citação está completamente errado.

Não só escusas de fazer mais pedidos à base de dados como não usas mais instruções... usas brutalmente menos, e a lógica é muito mais simples.

Humm... Ou não me fiz perceber ou não compreendeste. Vou explicar novamente.

O mysql_insert_id aplica-se apenas a um único insert, no caso do exemplo tens de fazer dois inserts e: ou ambos funcionam ou então a transacção é abortada e não há nenhum insert na BD.

Se não fizeres isso de forma transaccional usando as funcionalidades que as BDs te dão vais ter de ser tu a controlar tudo à mão:

- fazer o primeiro insert, fazendo um pedido à bd

- verificar se deu erro

- se não deu erro fazer o segundo insert usando o id chave do primeiro, fazendo um pedido à bd

- verificar se deu erro

- se deu erro tens de desfazer tudo o que havias feito até à data, que neste caso é eliminar o primeiro insert, fazendo um pedido à bd

Desta forma usas bastante mais lógica, de bd e de PHP, e tens de ser tu a artilhar tudo incluindo o desfazer das operações executadas. Como podes ver no "pesudocódigo", tens de fazer entre 2 a 3 chamadas à BD enquanto que se tiveres este procedimento no lado da BD, fazes apenas uma única chamada à BD. Se somares o custo de comunicação entre o PHP e a BD mais a interpetação dos comandos PHP, é bastante claro que a solução implementada unicamente do lado da BD tem óbvias vantagens.

N sei como raio é que dizes que é uma logica mais complicada  e que se mistura mais alhos com bugalhos... para usares o valor do ultimo id inserido sem recurso a esta função terias que misturar muitas mais coisas... pegar valores da base de dados e usar o php para compara-los etc etc.

Como expliquei acima, se a lógica de controlo for implementada por ti, estás a misturar alhos com bugalhos, estás a reinventar a roda, uma vez que implementas tu o controlo transaccional. O problema não é só encontrar o último ID, isso é "peanuts".

A questão da escolha não se poe neste caso. foi um membro que colocou uma questão... não lhe vamos dizer: "usa outro sgbd" principalmente qdo isto é uma coisa usada amplamente por milhares de programadores... na verdade mais pessoas devem usar a fução mysl_insert_id do que o oracle para fazer o que quer que seja, por exemplo.

O uso de outro SGBD nunca deve ser posto de lado, mas neste caso não me parece que seja necessário uma mudança drástica desse tipo. Quanto ao enorme uso, meu caro, se a maioria se atirar a um poço também te atiras? Quanto aos milhares de programadores, é como já referi acima, lá porque se fez uma escolha tecnicae tecnológica, se ela não serve, ficar a viver com ela para sempre é um erro maior do que a má escolha feita inicialmente.

Não percebi o que queres dizer com mais pessoas devem usar a função em causa do que Oracle para fazer seja o que for... O que queres dizer com isso e que raio de comparação é essa?

O ultimo paragrafo, para alem de estar incorrecto ( já vou explicar porque ) não se aplica neste caso.

fazes um query usando  a função mysql_query()... se for  bem sucedido gera-se um resulset, seguidamente pegas nele e extrais o id inserido... se a inserção tiver falhado o php nem sequer te vai compilar o próximo pedido, pelo que não martelas nada para a base de dados em situação alguma.

Isto é o mais simples possivel... e não acho que é misturar logica nenhuma pois simplesmente dás instruções por fora e não te preocupas com a base de dados que ela fa o trablho dela.

O que disseste não se aplica neste caso pois a o segundo query só é execurtado se o primeiro for bem sucedido, o que garante conformidade dos dados em qq situação.

Tens milhentas outras coisas além de inserts. O exemplo que dei parece-me ser o mais claro para que se compreenda o problema e a importância da escolha da solução.

Pa... eu n quero ser o teimoso cá do sitio, estou a dizer isto porque acho que complicaste demasiado uma coisa simples... n é puxar a brasa a minha sardinha nem nada, por isso é que estou a fundamentar. Qdo clicar em "enviar" o SMF lá vai fazer mais uma vez o que estou a dizer.. e não usa o método que disseste pela simples razão que o php já trata disso de forma eficiente do que qq código escrito em php por parte do programador, visto a função tar implementtada em mais baixo nivel.

Compreendo o que dizes, no entanto creio que não compliquei a questão, o próprio nuno_couto referiu isso, e estando num fórum onde o objectivo é aprender, acho redutor dar uma única resposta em vez de mostrar um leque de possíveis soluções.

Pa... eu sei o que estou a dizer e n é para te chatear ou contrariar nem nada disso... de resto podes fazer o teste... cria duas bds clones usa massivamente o teu método e o meu para fazer insersoes... faz um script que soma todos os tempos de execução e numero de queries e vê por ti próprio o preço de não simplificar as coisas.

Não tenho nenhum stress contigo. Como disse acima, este fórum serve para discutir e aprender, e foi exactamente à custa dessa discussão que já aprendi aqui o uso do mysql_insert_id, que desconhecia.  🙂

Sou apologista de discussões, só assim as coisas evoluem, porque tu sabes umas coisas que eu não sei, e eu sei umas coisas que tu não sabes, e em conjunto sabemos os dois muito mais, é esse o espírito com que participo neste fórum. 😄

Quanto à performance, não te iludas, uma solução que implique dois comandos de SQL do lado do PHP, quando implementada do lado da BD é sempre mais eficiente, nem que seja o facto de não tens o custo de fazer várias chamadas. Há menos de 6 meses estive a fazer testes de performance entre DB2, Oracle e uma cena implementada em Java e sei bem do que estou a falar. 😄

10 REM Generation 48K!
20 INPUT "URL:", A$
30 IF A$(1 TO 4) = "HTTP" THEN PRINT "400 Bad Request": GOTO 50
40 PRINT "404 Not Found"
50 PRINT "./M6 @ Portugal a Programar."

 

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.