|
FORUMS PROFESSIONNELS WINDEV, WEBDEV et WINDEV Mobile |
| | | | | |
| [WD8] Acces natif Oracle - Requete imbriquée |
| Débuté par ethic.adl, 29 déc. 2004 15:58 - 14 réponses |
| |
| | | |
|
| |
| Posté le 29 décembre 2004 - 15:58 |
Bonjour,
Si l'un de vous possède l'acces natif Oracle, pourrait-il tester l'exemple suivant : (Pour ce test j'utilise le schema SCOTT et la table EMP présent en général à la création de la base.)
LOCAL lReqNom,lReqSQL,lENAME1,lENAME2 sont des chaînes
SQLConnecte(VotreSource,"SCOTT","TIGER","","ORACLE") // Connexion à oracle via l'accès natif
lReqNom="Requete_1" lReqSQL="INSERT INTO EMP (EMPNO,ENAME) VALUES (8888,'Nom8888')" SQLTransaction(sqlDébut) // Debut de transaction 1 SQLExec(lReqSQL,lReqNom) // Execution de la requete SQLFerme(lReqNom) // Fermeture de la requete
lReqNom="Requete_2" lReqSQL="INSERT INTO EMP (EMPNO,ENAME) VALUES (9999,'Nom9999')" SQLTransaction(sqlDébut) // Debut de transaction 2 SQLExec(lReqSQL,lReqNom) // Execution de la requete SQLFerme(lReqNom) // Fermeture de la requete SQLTransaction(sqlFin) // Validation de transaction 2
SQLTransaction(sqlAnnule) // Annulation de la transaction 1
lReqNom="Requete_4" lReqSQL="SELECT ENAME FROM EMP WHERE EMPNO™99" SQLExec(lReqSQL,lReqNom) SQLAssocie(lReqNom,lENAME2) SQLPremier(lReqNom) SQLFerme(lReqNom) Info("2 EMP.ENAME = "+lENAME2) // la requete validee par la transaction 2 à disparue !!
SQLDeconnecte()
et me dire s'il constate que les données de la "Requete_2" ont disparus.
Version de windev : 01-80315p (Interne), 8.00Dha (Produit) Version accès natif WD80ORA.DLL : 60-80310F (Interne), 8.00Bca (Produit) Version Oracle BdD et Client : Oracle 9i release 9.2.0.1.0
Merci d'avance. |
| |
| |
| | | |
|
| | |
| |
| Posté le 29 décembre 2004 - 18:20 |
Il est très déconseillé d'imbriquer les transactions!!!
Pourquoi ne pas tout faire dans une même transaction?
Attention avec les transactions on crée implicitement des verrous sur les tables et donc une mauvaise gestion des transactions peut être désastreuse... Nous y avons goutés pendant plus de 3 mois!!
Bon courage te bonne année. |
| |
| |
| | | |
|
| | |
| |
| Posté le 29 décembre 2004 - 18:20 |
Il est très déconseillé d'imbriquer les transactions!!!
Pourquoi ne pas tout faire dans une même transaction?
Attention avec les transactions on crée implicitement des verrous sur les tables et donc une mauvaise gestion des transactions peut être désastreuse... Nous y avons goutés pendant plus de 3 mois!!
Bon courage te bonne année. |
| |
| |
| | | |
|
| | |
| |
| Posté le 30 décembre 2004 - 12:49 |
J'ai essaye votre requete, aucune ligne est insere !
Il me semble que l'utilisation de SQLTransaction(sqlDébut) dans Requete_2 n'est pas correct A chaque SQLTransaction(sqlDébut) Terminer la transaction : soit en validant les opérations effectuées (fonction SQLTransaction(SQLFIN)) soit en annulant les opérations effectuées (fonction SQLTransaction(SQLAnnule))
Utiliser SQLPlus ou WDSql pour interroger si le Nom8888 de la Requete_1 est bien insere ? |
| |
| |
| | | |
|
| | |
| |
| Posté le 01 janvier 2005 - 13:07 |
Bonjour,
Si l'un de vous possède l'acces natif Oracle, pourrait-il tester l'exemple suivant : (Pour ce test j'utilise le schema SCOTT et la table EMP présent en général à
la création de la base.)
LOCAL lReqNom,lReqSQL,lENAME1,lENAME2 sont des chaînes
SQLConnecte(VotreSource,"SCOTT","TIGER","","ORACLE") // Connexion à oracle via l'accès natif
lReqNom="Requete_1" lReqSQL="INSERT INTO EMP (EMPNO,ENAME) VALUES (8888,'Nom8888')" SQLTransaction(sqlDébut) // Debut de transaction 1 SQLExec(lReqSQL,lReqNom) // Execution de la requete SQLFerme(lReqNom) // Fermeture de la requete
là on insert 8888
lReqNom="Requete_2" lReqSQL="INSERT INTO EMP (EMPNO,ENAME) VALUES (9999,'Nom9999')"
> SQLTransaction(sqlDébut) // Debut de transaction 2
là où le problème commence !!!!!
SQLExec(lReqSQL,lReqNom) // Execution de la requete SQLFerme(lReqNom) // Fermeture de la requete
là on insert 9999
> SQLTransaction(sqlFin) // Validation de transaction 2
je commit...
SQLTransaction(sqlAnnule) // Annulation de la transaction 1
je rollback
> et me dire s'il constate que les données de la "Requete_2" ont disparus.
Je pense que celà vient tout simplement de votre mode de programmation. Comme vous pouvez le constater il n'y a pas de notion de commit à la requête mais bien à la transaction en cours. La question à se poser serait plutot : que fait un SQLTransaction(sqlDébut) alors que j'en ai déjà une en cours ? il rollback la précédente ou il se dit ok, une existe déjà je la réutilise.
J'ai fais le test avec l'accès Oracle4WD, je retrouve mes deux enregistrements insérés à la fin. Tout simplement parce que cet accès réutilise la transaction en cours.
Pour moi avant d'être un problème de l'accès natif (pourquoi un rollback annule tout alors qu'il y a eu un commit avant), c'est plus la philosophie de programmation qui est à revoir.
-- Emmanuel Lecoester |
| |
| |
| | | |
|
| | |
| |
| Posté le 03 janvier 2005 - 11:34 |
Bonjour,
Si l'un de vous possède l'acces natif Oracle, pourrait-il tester l'exemple suivant : (Pour ce test j'utilise le schema SCOTT et la table EMP présent en général à
la création de la base.)
LOCAL lReqNom,lReqSQL,lENAME1,lENAME2 sont des chaînes
SQLConnecte(VotreSource,"SCOTT","TIGER","","ORACLE") // Connexion à oracle via l'accès natif
lReqNom="Requete_1" lReqSQL="INSERT INTO EMP (EMPNO,ENAME) VALUES (8888,'Nom8888')" SQLTransaction(sqlDébut) // Debut de transaction 1 SQLExec(lReqSQL,lReqNom) // Execution de la requete SQLFerme(lReqNom) // Fermeture de la requete
Pour moi j'ai une autre interpretation :
SQLTransaction(sqlDebut) met une borne de debut et attend une validation ou une annulation correspond à un SAVEPOINT dans oracle il n'est plus en mode autocommit on.
le 8888 n'est pas encore insere
lReqNom="Requete_2" lReqSQL="INSERT INTO EMP (EMPNO,ENAME) VALUES (9999,'Nom9999') "
> SQLTransaction(sqlDébut) // Debut de transaction 2
Je ne sais pas si oracle accepte une 2eme SQLTransaction(sqlDébut) c'est a dire un 2eme savepoint sans valider ni valider la premiere, s'il n'accepte pas ca veut dire il constate une anomalie et ignore le traitement jusqu'a SQLTransaction(sqlFin) ou SQLTransaction(sqlAnnule) c'est pouquoi les 2 enregistements ne sont inseres.
SQLTransaction(sqlFin) // Validation de transaction 2 S'il existe un seul savepoint la validation est pour transaction 1 et 2
> SQLTransaction(sqlAnnule) // Annulation de la transaction 1 Logiquement, on ne peut plus annuler ce qu'on a valide je suis d'accord sur ce point.
Pour moi est ce que Oracle4WD et acces natif oracle de Windev n'interprete pas de la meme facon ou est ce que les differents versions evoluent pour creer cette difference ? 3 constats differents !!! : - aucune lignes inserees - une seule ligne est inseree - 2 lignes sont inserees
Attention sous oracle, les enregistrements sont visible que par leur createur, les autres utilisateur verront que s'ils sont valides. |
| |
| |
| | | |
|
| | |
| |
| Posté le 03 janvier 2005 - 20:39 |
"Paul LE" <le.paul@promoplast.com> a écrit dans le message de news:41d9076c$1@news.pcsoft.fr...
Bonjour, Si l'un de vous possède l'acces natif Oracle, pourrait-il tester l'exemple
suivant : (Pour ce test j'utilise le schema SCOTT et la table EMP présent en général
à la création de la base.)
LOCAL lReqNom,lReqSQL,lENAME1,lENAME2 sont des chaînes
SQLConnecte(VotreSource,"SCOTT","TIGER","","ORACLE") // Connexion à oracle
via l'accès natif
lReqNom="Requete_1" lReqSQL="INSERT INTO EMP (EMPNO,ENAME) VALUES (8888,'Nom8888')" SQLTransaction(sqlDébut) // Debut de transaction 1 SQLExec(lReqSQL,lReqNom) // Execution de la requete SQLFerme(lReqNom) // Fermeture de la requete
Pour moi j'ai une autre interpretation : SQLTransaction(sqlDebut) met une borne de debut et attend une validation ou une annulation correspond à un SAVEPOINT > dans oracle il n'est plus en mode autocommit on.
OUI mais c'est une fonctionnalité uniquement disponible à partir de la 8i : gérer une transaction dans une une autre transaction. Une transaction est par défaut liée à la session en cours. Donc :
début session => nouvelle transaction et AUCUN savepoint de placé nouvelle transaction insert 8888 nouvelle transaction =>>>> il fait quoi ???? voir les cas insert 9999 commit rollback
cas 1 : rien du tout, il réutilise la transaction : 9999 + 8888 insérée cas 2 : il annule l'ancienne (le 8888 est rollbacké par défaut) et crée la nouvelle : 9999 insérée cas 3 : il place un savepoint : ok mais dans ce cas cela implique un rollaback savepoint ... et le commit entre les deux donnent koi ? cas 4 : il crée une sous transaction : 9999 insérée
Pour moi est ce que Oracle4WD et acces natif oracle de Windev n'interprete pas de la meme facon ou est ce que les differents versions
evoluent pour creer cette difference ? 3 constats differents !!! : - aucune lignes inserees
je ne vois pas comment vu la présence d'un commit avec le rollback.
> - une seule ligne est inseree
cas 2 ou 4
- 2 lignes sont inserees
cas1 : c'est sur c'est celui de Oracle4WD que ce soit du Oracle 7, 8 ou 9 !
ne pas oublié le post de départ, ce sont les données 9999 qui sont pardues. Dans chacun de nos cas de figure ce sont les données 8 qui portent à discussion !
Une chose est sure, il faudrait la réponse du ST sur le mode de fonctionnement de l'accès natif de l'éditeur 
> Attention sous oracle, les enregistrements sont visible que par leur createur, les autres utilisateur verront que s'ils sont valides.
oui mais le commit à eu lieu 
-- Emmanuel Lecoester |
| |
| |
| | | |
|
| | |
| |
| Posté le 04 janvier 2005 - 13:01 |
J'ai la version 8.0.4 d'oracle c'est pourquoi je ne peux pas gerer une transaction dans une autre transaction.
Je pense pour pouvoir gerer une transaction dans une transaction SQLTransaction(sqlDébut), SQLTransaction(sqlFin), SQLTransaction(sqlAnnule) ne suffisent plus. Il faut que SQLTransaction(sqlDébut) puisse aussi indiquer le numero de transaction laquelle SQLTransaction(sqlFin) fait reference pour valider ou SQLTransaction(sqlAnnule) pour annuler.
j'ai fait 3 tests suivants: ---------- 1/ j'ai 2 lignes inserees ---------- commande est chaîne SQLTransaction(sqlDébut) // debut session marquer par un savepoint commande = "Insert into emp (empno, ename) values (8888,'NOM8888')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") commande = "Insert into emp (empno, ename) values (9999,'NOM9999')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") SQLTransaction(sqlFin) // validation session ---------- 2/ j'ai aucune ligne inseree ---------- commande est chaîne SQLTransaction(sqlDébut) // debut session marquer par un savepoint commande = "Insert into emp (empno, ename) values (8888,'NOM8888')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") commande = "Insert into emp (empno, ename) values (9999,'NOM9999')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") SQLTransaction(sqlAnnule) // annulation session ---------- 3/ j'ai aucune ligne inseree ---------- commande est chaîne SQLTransaction(sqlDébut) // debut session marquer par un savepoint commande = "Insert into emp (empno, ename) values (8888,'NOM8888')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") SQLTransaction(sqlDébut) // transaction marquer par un 2eme savepoint (anomalie ???) commande = "Insert into emp (empno, ename) values (9999,'NOM9999')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") SQLTransaction(sqlFin) // validation session
Pourtant je valide bien la session par SQLTransaction(sqlFin) !
----------
ne pas oublié le post de départ, ce sont les données 9999 qui sont pardues. Je prefere qu'il reverifie pour confirmer.
J'ai aussi oracle4wd, mais je ne sais pas comment faire pour pour placer SQLTransaction(sqlDébut) pour faire le test.
???? Commande = "Insert into emp (empno, ename) values (8888,'NOM8888')" retcode = oracle4wd:mySQLExec(Commande,1) IF (retcode) THEN info("OK") END oracle4wd:mySQLFerme(1) |
| |
| |
| | | |
|
| | |
| |
| Posté le 04 janvier 2005 - 19:20 |
Merci tout d'abord pour vos réponses. En fait j'ai voulu simplifier mon probleme en soumettant cet exemple (illogique surement). Mon vrai problème est le suivant. (Je gere une appli multi-utilisateurs).
1) J'ouvre une fenetre 1 qui bloque une table A avec SQLTransaction(SQLDebut)+SQLBloque()
2) J'ouvre une fenetre 2 qui bloque une table B avec SQLTransaction(SQLDebut)+SQLBloque() et j'insere des enreg dans la table B que je valide avec SQLTransaction(SQLFin)
3) Je reviens dans fenetre 1 et je debloque la table A avec SQLTransaction(SQLAnnule).
4) Je retourne dans fenetre 2 et là je constate que les enreg inserés dans la table B ont disparu.
Il existe une solution pour régler ce probleme. Il suffit d'utiliser dans le point 3 SQLTransaction(SQLFin) et la tout rentre dans l'ordre. Il n'empeche que je m'explique pas pourquoi avec SQLTransaction(SQLAnnule) cela ne marche pas pareil ????
Bonne année à vous.
"Paul LE" <le.paul@promoplast.com> a écrit dans le message de news: 41da6d3e@news.pcsoft.fr...
J'ai la version 8.0.4 d'oracle c'est pourquoi je ne peux pas gerer une transaction dans une autre transaction.
Je pense pour pouvoir gerer une transaction dans une transaction SQLTransaction(sqlDébut), SQLTransaction(sqlFin), SQLTransaction(sqlAnnule) ne suffisent plus. Il faut que SQLTransaction(sqlDébut) puisse aussi indiquer le numero de transaction laquelle SQLTransaction(sqlFin) fait reference pour valider ou SQLTransaction(sqlAnnule) pour annuler.
j'ai fait 3 tests suivants: ---------- 1/ j'ai 2 lignes inserees ---------- commande est chaîne SQLTransaction(sqlDébut) // debut session marquer par un savepoint commande = "Insert into emp (empno, ename) values (8888,'NOM8888')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") commande = "Insert into emp (empno, ename) values (9999,'NOM9999')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") SQLTransaction(sqlFin) // validation session ---------- 2/ j'ai aucune ligne inseree ---------- commande est chaîne SQLTransaction(sqlDébut) // debut session marquer par un savepoint commande = "Insert into emp (empno, ename) values (8888,'NOM8888')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") commande = "Insert into emp (empno, ename) values (9999,'NOM9999')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") SQLTransaction(sqlAnnule) // annulation session ---------- 3/ j'ai aucune ligne inseree ---------- commande est chaîne SQLTransaction(sqlDébut) // debut session marquer par un savepoint commande = "Insert into emp (empno, ename) values (8888,'NOM8888')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") SQLTransaction(sqlDébut) // transaction marquer par un 2eme savepoint (anomalie ???) commande = "Insert into emp (empno, ename) values (9999,'NOM9999')" SI PAS SQLExec(commande, "REQPAR") ALORS SQLErreur("REQPAR", commande) FIN SQLFerme("REQPAR") SQLTransaction(sqlFin) // validation session
Pourtant je valide bien la session par SQLTransaction(sqlFin) !
----------
ne pas oublié le post de départ, ce sont les données 9999 qui sont pardues. Je prefere qu'il reverifie pour confirmer.
J'ai aussi oracle4wd, mais je ne sais pas comment faire pour pour placer SQLTransaction(sqlDébut) pour faire le test.
???? Commande = "Insert into emp (empno, ename) values (8888,'NOM8888')" retcode = oracle4wd:mySQLExec(Commande,1) IF (retcode) THEN info("OK") END oracle4wd:mySQLFerme(1)
|
| |
| |
| | | |
|
| | |
| |
| Posté le 04 janvier 2005 - 20:42 |
"Paul LE" <le.paul@promoplast.com> a écrit dans le message de news:41da6d3e@news.pcsoft.fr...
J'ai la version 8.0.4 d'oracle c'est pourquoi je ne peux pas gerer une
transaction dans une autre transaction.
Je pense pour pouvoir gerer une transaction dans une transaction
SQLTransaction(sqlDébut), SQLTransaction(sqlFin), SQLTransaction(sqlAnnule) ne suffisent plus. > Il faut que SQLTransaction(sqlDébut) puisse aussi indiquer le numero de transaction laquelle SQLTransaction(sqlFin) fait reference
pour valider ou SQLTransaction(sqlAnnule) pour annuler.
idem
> J'ai aussi oracle4wd, mais je ne sais pas comment faire pour pour placer SQLTransaction(sqlDébut) pour faire le test.
???? Commande = "Insert into emp (empno, ename) values (8888,'NOM8888')" retcode = oracle4wd:mySQLExec(Commande,1) IF (retcode) THEN info("OK") END oracle4wd:mySQLFerme(1)
Oracle4WD:mySQLTransaction(sqlDébut) Oracle4WD:mySQLExec("INSERT INTO EMP (EMPNO,ENAME) VALUES (8888,'Nom8888')",0) Oracle4WD:mySQLFerme(0)
Oracle4WD:mySQLTransaction(sqlDébut) Oracle4WD:mySQLExec("INSERT INTO EMP (EMPNO,ENAME) VALUES (9999,'Nom9999')",0) Oracle4WD:mySQLFerme(0) Oracle4WD:mySQLTransaction(sqlFin) // Validation de transaction 2
Oracle4WD:mySQLTransaction(sqlAnnule) // Annulation de la transaction 1
Normalement et si tu es en autocommit off par défaut, si tu ne fais que le exec et le ferm, dès que tu sors de ton test, c'est rollback automatique.
@+
-- Emmanuel |
| |
| |
| | | |
|
| | |
| |
| Posté le 04 janvier 2005 - 20:46 |
1) J'ouvre une fenetre 1 qui bloque une table A avec SQLTransaction(SQLDebut)+SQLBloque()
2) J'ouvre une fenetre 2 qui bloque une table B avec SQLTransaction(SQLDebut)+SQLBloque() et j'insere des enreg dans la table B que je valide avec SQLTransaction(SQLFin)
3) Je reviens dans fenetre 1 et je debloque la table A avec SQLTransaction(SQLAnnule).
4) Je retourne dans fenetre 2 et là je constate que les enreg inserés dans la table B ont disparu.
Pourquoi ne pas faire :
SQLTransaction(SQLDebut)+SQLBloqueTableA() c'est un 'for update nowait table A' SQLBloqueTableB() c'est un 'for update nowait table B' SQLTransaction(SQLFin)
de tête c'est soit un commit, soit un rollback que retire ton lock posé par ton 'for update nowait'
c'est vrai que si tu as une erreur dans l'insert tableB il rollback tout donc tu perd ton premier lock mais est-ce si génant ?
-- Emmanuel Lecoester |
| |
| |
| | | |
|
| | |
| |
| Posté le 05 janvier 2005 - 10:33 |
OK pour ta solution mais si j'ouvre B et A ? Mon application compte + de 200 fenêtres. Bref j'ai soumis ce probleme au ST et j'attends des eclaircissements.
"Emmanuel Lecoester" <elecoest@netcourrier.com> a écrit dans le message de news: 41dadb48$1@news.pcsoft.fr...
1) J'ouvre une fenetre 1 qui bloque une table A avec SQLTransaction(SQLDebut)+SQLBloque()
2) J'ouvre une fenetre 2 qui bloque une table B avec SQLTransaction(SQLDebut)+SQLBloque() et j'insere des enreg dans la table B que je valide avec SQLTransaction(SQLFin)
3) Je reviens dans fenetre 1 et je debloque la table A avec SQLTransaction(SQLAnnule).
4) Je retourne dans fenetre 2 et là je constate que les enreg inserés dans la table B ont disparu.
Pourquoi ne pas faire : SQLTransaction(SQLDebut)+SQLBloqueTableA() c'est un 'for update nowait table A' SQLBloqueTableB() c'est un 'for update nowait table B' SQLTransaction(SQLFin) de tête c'est soit un commit, soit un rollback que retire ton lock posé par ton 'for update nowait' c'est vrai que si tu as une erreur dans l'insert tableB il rollback tout donc tu perd ton premier lock mais est-ce si génant ? -- Emmanuel Lecoester |
| |
| |
| | | |
|
| | |
| |
| Posté le 05 janvier 2005 - 12:25 |
| |
| |
| | | |
|
| | |
| |
| Posté le 05 janvier 2005 - 20:03 |
"Alain" <ethic.adl@wanadoo.fr> a écrit dans le message de news:41db9d22@news.pcsoft.fr...
OK pour ta solution mais si j'ouvre B et A ? Mon application compte + de 200 fenêtres.
dans ces cas là je ferais une réponse digne d'une ST : il me faut un exemple devant les yeux opur comprendre effectivement ton problème et trouver une solution technique 
> Bref j'ai soumis ce probleme au ST et j'attends des eclaircissements. |
| |
| |
| | | |
|
| | |
| |
| Posté le 06 janvier 2005 - 18:23 |
Je viens de recevoir une réponse su ST disant qu'une recherche est actuellement en cours afin de connaitre le fonctionnement à attendre d'un tel traitement. Un espoir quand même.
Wait and see
"Emmanuel Lecoester" <elecoest@netcourrier.com> a écrit dans le message de news: 41dc228c@news.pcsoft.fr...
"Alain" <ethic.adl@wanadoo.fr> a écrit dans le message de news:41db9d22@news.pcsoft.fr... OK pour ta solution mais si j'ouvre B et A ? Mon application compte + de 200 fenêtres.
dans ces cas là je ferais une réponse digne d'une ST : il me faut un exemple devant les yeux opur comprendre effectivement ton problème et trouver une solution technique  Bref j'ai soumis ce probleme au ST et j'attends des eclaircissements.
|
| |
| |
| | | |
|
| | | | |
| | |
| | |
| |
|
|
|