diff --git a/README.md b/README.md index 788ff1eb..f29a27f4 100644 --- a/README.md +++ b/README.md @@ -215,3 +215,11 @@ Before submitting design contributions, please discuss them with the Standard Eb else print('Bar!'); ```` + +- SQL statements have the first keyword in ALL CAPS to facilitate syntax highlighting; however subsequent keywords are all lowercase. + + ````sql + SELECT * from Users where Username = 'Foo' + ```` + +- When using SQL to insert a record and later retrieve the last inserted ID, use the `insert ... returning` construct, and not `insert` followed by `select last_insert_id()`. diff --git a/lib/Artist.php b/lib/Artist.php index 14a08b17..6feba509 100644 --- a/lib/Artist.php +++ b/lib/Artist.php @@ -268,14 +268,13 @@ class Artist{ */ public function Create(): void{ $this->Validate(); - Db::Query(' + $this->ArtistId = Db::QueryInt(' INSERT into Artists (Name, UrlName, DeathYear) values (?, ?, ?) + returning ArtistId ', [$this->Name, $this->UrlName, $this->DeathYear]); - - $this->ArtistId = Db::GetLastInsertedId(); } /** diff --git a/lib/Artwork.php b/lib/Artwork.php index e3cbc11f..98fde395 100644 --- a/lib/Artwork.php +++ b/lib/Artwork.php @@ -720,7 +720,7 @@ class Artwork{ $this->Artist = Artist::GetOrCreate($this->Artist); - Db::Query(' + $this->ArtworkId = Db::QueryInt(' INSERT into Artworks (ArtistId, Name, UrlName, CompletedYear, CompletedYearIsCirca, Created, Updated, Status, SubmitterUserId, ReviewerUserId, MuseumUrl, PublicationYear, PublicationYearPageUrl, CopyrightPageUrl, ArtworkPageUrl, IsPublishedInUs, @@ -745,13 +745,12 @@ class Artwork{ ?, ?, ?) + returning ArtworkId ', [$this->Artist->ArtistId, $this->Name, $this->UrlName, $this->CompletedYear, $this->CompletedYearIsCirca, $this->Created, $this->Updated, $this->Status, $this->SubmitterUserId, $this->ReviewerUserId, $this->MuseumUrl, $this->PublicationYear, $this->PublicationYearPageUrl, $this->CopyrightPageUrl, $this->ArtworkPageUrl, $this->IsPublishedInUs, $this->EbookUrl, $this->MimeType, $this->Exception, $this->Notes] ); - $this->ArtworkId = Db::GetLastInsertedId(); - foreach($this->Tags as $tag){ Db::Query(' INSERT into ArtworkTags (ArtworkId, TagId) diff --git a/lib/ArtworkTag.php b/lib/ArtworkTag.php index 46d59ad7..20ee989b 100644 --- a/lib/ArtworkTag.php +++ b/lib/ArtworkTag.php @@ -65,13 +65,14 @@ class ArtworkTag extends Tag{ public function Create(): void{ $this->Validate(); - Db::Query(' + $this->TagId = Db::QueryInt(' INSERT into Tags (Name, UrlName, Type) values (?, ?, ?) + returning + TagId ', [$this->Name, $this->UrlName, $this->Type]); - $this->TagId = Db::GetLastInsertedId(); } /** diff --git a/lib/Collection.php b/lib/Collection.php index f22d532a..e6386c1d 100644 --- a/lib/Collection.php +++ b/lib/Collection.php @@ -162,13 +162,13 @@ class Collection{ public function Create(): void{ $this->Validate(); - Db::Query(' + $this->CollectionId = Db::QueryInt(' INSERT into Collections (Name, UrlName, Type) values (?, ?, ?) + returning CollectionId ', [$this->Name, $this->UrlName, $this->Type]); - $this->CollectionId = Db::GetLastInsertedId(); } /** diff --git a/lib/Db.php b/lib/Db.php index b6b5d3cf..5205f70c 100644 --- a/lib/Db.php +++ b/lib/Db.php @@ -11,10 +11,13 @@ class Db{ /** * Returns a single integer value for the first column database query result. * - * This is useful for queries that return a single integer as a result, like `count(*)` or `sum(*)`. + * This is useful for queries that return a single integer as a result, like `count(*)`, `sum(*)`, or `insert ... returning`. * * @param string $query * @param array $args + * + * @throws Exceptions\DuplicateDatabaseKeyException If a unique key constraint has been violated. + * @throws Exceptions\DatabaseQueryException If an error occurs during execution of the query. */ public static function QueryInt(string $query, array $args = []): int{ $result = static::Query($query, $args); @@ -33,6 +36,8 @@ class Db{ * * @param string $query * @param array $args + * + * @throws Exceptions\DatabaseQueryException If an error occurs during execution of the query. */ public static function QueryFloat(string $query, array $args = []): float{ $result = static::Query($query, $args); @@ -51,6 +56,8 @@ class Db{ * * @param string $query * @param array $args + * + * @throws Exceptions\DatabaseQueryException If an error occurs during execution of the query. */ public static function QueryBool(string $query, array $args = []): bool{ $result = static::Query($query, $args); @@ -145,8 +152,8 @@ class Db{ * * @return array An array of objects of type `$class`, or `stdClass` if `$class` is `null`. * - * @throws Exceptions\DuplicateDatabaseKeyException When a unique key constraint has been violated. - * @throws Exceptions\DatabaseQueryException When an error occurs during execution of the query. + * @throws Exceptions\DuplicateDatabaseKeyException If a unique key constraint has been violated. + * @throws Exceptions\DatabaseQueryException If an error occurs during execution of the query. */ public static function Query(string $sql, array $params = [], string $class = 'stdClass'): array{ $handle = static::PreparePdoHandle($sql, $params); @@ -202,7 +209,7 @@ class Db{ * @return array An array of `$class`. * * @throws Exceptions\MultiSelectMethodNotFoundException If the class doesn't have a `FromMultiTableRow()` method. - * @throws Exceptions\DatabaseQueryException When an error occurs during execution of the query. + * @throws Exceptions\DatabaseQueryException If an error occurs during execution of the query. */ public static function MultiTableSelect(string $sql, array $params, string $class): array{ if(!method_exists($class, 'FromMultiTableRow')){ @@ -278,7 +285,7 @@ class Db{ * * @return array> An array of `$class` if `$class` is not `null`, otherwise an array of rows of the form `["LeftTableName" => $stdClass, "RightTableName" => $stdClass]`. * - * @throws Exceptions\DatabaseQueryException When an error occurs during execution of the query. + * @throws Exceptions\DatabaseQueryException If an error occurs during execution of the query. */ public static function MultiTableSelectGeneric(string $sql, array $params): array{ $handle = static::PreparePdoHandle($sql, $params); @@ -317,7 +324,7 @@ class Db{ * * @return \PDOStatement The `\PDOStatement` to be used to execute the query. * - * @throws Exceptions\DatabaseQueryException When an error occurs during execution of the query. + * @throws Exceptions\DatabaseQueryException If an error occurs during execution of the query. */ protected static function PreparePdoHandle(string $sql, array $params): \PDOStatement{ try{ @@ -365,7 +372,7 @@ class Db{ * * @return array An array of objects of type `$class`, or `stdClass` if `$class` is `null`. * - * @throws \PDOException When an error occurs during execution of the query. + * @throws \PDOException If an error occurs during execution of the query. */ protected static function ExecuteQuery(\PDOStatement $handle, string $class = 'stdClass'): array{ $handle->execute(); @@ -445,7 +452,7 @@ class Db{ * * @return array|array> An array of `$class` if `$class` is not `stdClass`, otherwise an array of rows of the form `["LeftTableName" => $stdClass, "RightTableName" => $stdClass]`. * - * @throws \PDOException When an error occurs during execution of the query. + * @throws \PDOException If an error occurs during execution of the query. */ protected static function ExecuteMultiTableSelect(\PDOStatement $handle, string $class): array{ $handle->execute(); @@ -569,29 +576,6 @@ class Db{ } } - /** - * Get the ID of the last row that was inserted during this database connection. - * - * @return int The ID of the last row that was inserted during this database connection. - * - * @throws Exceptions\DatabaseQueryException When the last inserted ID can't be determined. - */ - public static function GetLastInsertedId(): int{ - try{ - $id = static::$Link->lastInsertId(); - } - catch(\PDOException){ - $id = false; - } - - if($id === false || $id == '0'){ - throw new Exceptions\DatabaseQueryException('Couldn\'t get last insert ID.'); - } - else{ - return intval($id); - } - } - /** * Create a detailed `Exceptions\DatabaseQueryException` from a `\PDOException`. * diff --git a/lib/Ebook.php b/lib/Ebook.php index 62ec7306..5a68e509 100644 --- a/lib/Ebook.php +++ b/lib/Ebook.php @@ -1777,7 +1777,7 @@ final class Ebook{ throw $error; } - Db::Query(' + $this->EbookId = Db::QueryInt(' INSERT into Ebooks (Identifier, WwwFilesystemPath, RepoFilesystemPath, KindleCoverUrl, EpubUrl, AdvancedEpubUrl, KepubUrl, Azw3Url, DistCoverUrl, Title, FullTitle, AlternateTitle, Description, LongDescription, Language, WordCount, ReadingEase, GitHubUrl, WikipediaUrl, @@ -1808,6 +1808,7 @@ final class Ebook{ ?, ?, ?) + returning EbookId ', [$this->Identifier, $this->WwwFilesystemPath, $this->RepoFilesystemPath, $this->KindleCoverUrl, $this->EpubUrl, $this->AdvancedEpubUrl, $this->KepubUrl, $this->Azw3Url, $this->DistCoverUrl, $this->Title, $this->FullTitle, $this->AlternateTitle, $this->Description, $this->LongDescription, @@ -1815,8 +1816,6 @@ final class Ebook{ $this->EbookCreated, $this->EbookUpdated, $this->TextSinglePageByteCount, $this->IndexableText, $this->IndexableAuthors, $this->IndexableCollections]); - $this->EbookId = Db::GetLastInsertedId(); - try{ $this->AddTags(); $this->AddLocSubjects(); diff --git a/lib/EbookTag.php b/lib/EbookTag.php index 4f907281..33d7483b 100644 --- a/lib/EbookTag.php +++ b/lib/EbookTag.php @@ -56,13 +56,13 @@ class EbookTag extends Tag{ public function Create(): void{ $this->Validate(); - Db::Query(' + $this->TagId = Db::QueryInt(' INSERT into Tags (Name, UrlName, Type) values (?, ?, ?) + returning TagId ', [$this->Name, $this->UrlName, $this->Type]); - $this->TagId = Db::GetLastInsertedId(); } diff --git a/lib/LocSubject.php b/lib/LocSubject.php index 36d4bf07..c5dfb150 100644 --- a/lib/LocSubject.php +++ b/lib/LocSubject.php @@ -39,11 +39,11 @@ class LocSubject{ public function Create(): void{ $this->Validate(); - Db::Query(' + $this->LocSubjectId = Db::QueryInt(' INSERT into LocSubjects (Name) values (?) + returning LocSubjectId ', [$this->Name]); - $this->LocSubjectId = Db::GetLastInsertedId(); } /** diff --git a/lib/Payment.php b/lib/Payment.php index c0f05cf2..cbc2d3d1 100644 --- a/lib/Payment.php +++ b/lib/Payment.php @@ -95,7 +95,7 @@ class Payment{ } try{ - Db::Query(' + $this->PaymentId = Db::QueryInt(' INSERT into Payments (UserId, Created, Processor, TransactionId, Amount, Fee, IsRecurring, IsMatchingDonation) values(?, ?, @@ -105,12 +105,11 @@ class Payment{ ?, ?, ?) + returning PaymentId ', [$this->UserId, $this->Created, $this->Processor, $this->TransactionId, $this->Amount, $this->Fee, $this->IsRecurring, $this->IsMatchingDonation]); } catch(Exceptions\DuplicateDatabaseKeyException){ throw new Exceptions\PaymentExistsException(); } - - $this->PaymentId = Db::GetLastInsertedId(); } } diff --git a/lib/Project.php b/lib/Project.php index 94c16d06..84a5414a 100644 --- a/lib/Project.php +++ b/lib/Project.php @@ -347,7 +347,7 @@ final class Project{ throw new Exceptions\ProjectExistsException(); } - Db::Query(' + $this->ProjectId = Db::QueryInt(' INSERT into Projects ( EbookId, @@ -384,10 +384,9 @@ final class Project{ ?, ? ) + returning ProjectId ', [$this->EbookId, $this->Status, $this->ProducerName, $this->ProducerEmail, $this->DiscussionUrl, $this->VcsUrl, NOW, NOW, $this->Started, $this->Ended, $this->ManagerUserId, $this->ReviewerUserId, $this->LastCommitTimestamp, $this->LastDiscussionTimestamp, $this->IsStatusAutomaticallyUpdated]); - $this->ProjectId = Db::GetLastInsertedId(); - // Notify the manager and reviewer. if($this->Status == Enums\ProjectStatusType::InProgress){ // The manager is also the reviewer, just send one email. diff --git a/lib/User.php b/lib/User.php index 18e196ea..555dd20f 100644 --- a/lib/User.php +++ b/lib/User.php @@ -251,20 +251,19 @@ class User{ } try{ - Db::Query(' + $this->UserId = Db::QueryInt(' INSERT into Users (Email, Name, Uuid, Created, PasswordHash) values (?, ?, ?, ?, ?) + returning UserId ', [$this->Email, $this->Name, $this->Uuid, $this->Created, $this->PasswordHash]); } catch(Exceptions\DuplicateDatabaseKeyException){ throw new Exceptions\UserExistsException(); } - - $this->UserId = Db::GetLastInsertedId(); } /**