Index: src/wp-includes/post.php
===================================================================
--- src/wp-includes/post.php	(revision 30364)
+++ src/wp-includes/post.php	(working copy)
@@ -3691,48 +3691,48 @@
 			do {
 				$alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
 				$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_ID ) );
 				$suffix++;
 			} while ( $post_name_check );
 			$slug = $alt_post_name;
 		}
 	} elseif ( is_post_type_hierarchical( $post_type ) ) {
 		if ( 'nav_menu_item' == $post_type )
 			return $slug;
 
 		/*
 		 * Page slugs must be unique within their own trees. Pages are in a separate
 		 * namespace than posts so page slugs are allowed to overlap post slugs.
 		 */
-		$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d AND post_parent = %d LIMIT 1";
+		$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1";
 		$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID, $post_parent ) );
 
 		/**
 		 * Filter whether the post slug would make a bad hierarchical post slug.
 		 *
 		 * @since 3.1.0
 		 *
 		 * @param bool   $bad_slug    Whether the post slug would be bad in a hierarchical post context.
 		 * @param string $slug        The post slug.
 		 * @param string $post_type   Post type.
 		 * @param int    $post_parent Post parent ID.
 		 */
 		if ( $post_name_check || in_array( $slug, $feeds ) || preg_match( "@^($wp_rewrite->pagination_base)?\d+$@", $slug )  || apply_filters( 'wp_unique_post_slug_is_bad_hierarchical_slug', false, $slug, $post_type, $post_parent ) ) {
 			$suffix = 2;
 			do {
 				$alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
-				$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_ID, $post_parent ) );
+				$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID, $post_parent ) );	
 				$suffix++;
 			} while ( $post_name_check );
 			$slug = $alt_post_name;
 		}
 	} else {
 		// Post slugs must be unique across all posts.
 		$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1";
 		$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID ) );
 
 		/**
 		 * Filter whether the post slug would be bad as a flat slug.
 		 *
 		 * @since 3.1.0
 		 *
 		 * @param bool   $bad_slug  Whether the post slug would be bad as a flat slug.
Index: tests/phpunit/tests/post.php
===================================================================
--- tests/phpunit/tests/post.php	(revision 30364)
+++ tests/phpunit/tests/post.php	(working copy)
@@ -1006,30 +1006,91 @@
 		$one = $this->factory->post->create( $args );
 		$args['post_type'] = 'post-type-2';
 		$two = $this->factory->post->create( $args );
 
 		$this->assertEquals( 'some-slug', get_post( $one )->post_name );
 		$this->assertEquals( 'some-slug', get_post( $two )->post_name );
 
 		$this->assertEquals( 'some-other-slug', wp_unique_post_slug( 'some-other-slug', $one, 'publish', 'post-type-1', 0 ) );
 		$this->assertEquals( 'some-other-slug', wp_unique_post_slug( 'some-other-slug', $one, 'publish', 'post-type-2', 0 ) );
 
 		_unregister_post_type( 'post-type-1' );
 		_unregister_post_type( 'post-type-2' );
 	}
 
 	/**
+	 * @ticket 30339
+	 */
+	function test_wp_unique_post_slug_with_hierarchy() {
+		register_post_type( 'post-type-1', array( 'hierarchical' => true ) );
+
+		$args = array(
+			'post_type' => 'post-type-1',
+			'post_name' => 'some-slug',
+			'post_status' => 'publish',
+		);
+		$one = $this->factory->post->create( $args );
+		$args['post_name'] = 'some-slug-2';
+		$two = $this->factory->post->create( $args );
+
+		$this->assertEquals( 'some-slug', get_post( $one )->post_name );
+		$this->assertEquals( 'some-slug-2', get_post( $two )->post_name );
+
+		$this->assertEquals( 'some-slug-3', wp_unique_post_slug( 'some-slug', 0, 'publish', 'post-type-1', 0 ) );
+
+		_unregister_post_type( 'post-type-1' );
+	}
+
+	/**
+	 * @ticket 18962
+	 */
+	function test_wp_unique_post_slug_with_hierarchy_and_attachments() {
+		register_post_type( 'post-type-1', array( 'hierarchical' => true ) );
+
+		$args = array(
+			'post_type' => 'post-type-1',
+			'post_name' => 'some-slug',
+			'post_status' => 'publish',
+		);
+		$one = $this->factory->post->create( $args );
+
+		$args = array(
+			'post_mime_type' => 'image/jpeg',
+			'post_type' => 'attachment',
+			'post_name' => 'image'
+		);
+		$attachment = $this->factory->attachment->create_object( 'image.jpg', $one, $args );
+
+		$args = array(
+			'post_type' => 'post-type-1',
+			'post_name' => 'image',
+			'post_status' => 'publish',
+			'post_parent' => $one
+		);
+		$two = $this->factory->post->create( $args );
+
+		$this->assertEquals( 'some-slug', get_post( $one )->post_name );
+		$this->assertEquals( 'image', get_post( $attachment )->post_name );
+		$this->assertEquals( 'image-2', get_post( $two )->post_name );
+
+		// 'image' can be a child of image-2
+		$this->assertEquals( 'image', wp_unique_post_slug( 'image', 0, 'publish', 'post-type-1', $two ) );
+
+		_unregister_post_type( 'post-type-1' );
+	}
+
+	/**
 	 * @ticket 21212
 	 */
 	function test_utf8mb3_post_saves_with_emoji() {
 		global $wpdb;
 		$_wpdb = new wpdb_exposed_methods_for_testing();
 
 		if ( 'utf8' !== $_wpdb->get_col_charset( $wpdb->posts, 'post_title' ) ) {
 			$this->markTestSkipped( 'This test is only useful with the utf8 character set' );
 		}
 
 		require_once( ABSPATH . '/wp-admin/includes/post.php' );
 
 		$post_id = $this->factory->post->create();
 
 		$data = array(
